VOL-2640 Restructure openolt-adapter repo to best practices
Change-Id: Icead31e8ecb82ec75a22e66361fbf83f80136589
diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go
new file mode 100644
index 0000000..0095575
--- /dev/null
+++ b/internal/pkg/config/config.go
@@ -0,0 +1,200 @@
+/*
+* 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"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "os"
+ "time"
+)
+
+// Open OLT default constants
+const (
+ EtcdStoreName = "etcd"
+ defaultInstanceid = "openOlt001"
+ defaultKafkaadapterhost = "127.0.0.1"
+ defaultKafkaadapterport = 9092
+ defaultKafkaclusterhost = "127.0.0.1"
+ defaultKafkaclusterport = 9094
+ defaultKvstoretype = EtcdStoreName
+ defaultKvstoretimeout = 5 //in seconds
+ defaultKvstorehost = "127.0.0.1"
+ defaultKvstoreport = 2379 // Consul = 8500; Etcd = 2379
+ defaultLoglevel = "DEBUG"
+ defaultBanner = false
+ defaultDisplayVersionOnly = false
+ defaultTopic = "openolt"
+ defaultCoretopic = "rwcore"
+ defaultEventtopic = "voltha.events"
+ defaultOnunumber = 1
+ defaultProbeHost = ""
+ defaultProbePort = 8080
+ defaultLiveProbeInterval = 60 * time.Second
+ defaultNotLiveProbeInterval = 5 * time.Second // Probe more frequently when not alive
+ //defaultHearbeatFailReportInterval is the time in seconds the adapter will keep checking the hardware for heartbeat.
+ defaultHearbeatCheckInterval = 30 * time.Second
+ // defaultHearbeatFailReportInterval is the time adapter will wait before updating the state to the core.
+ defaultHearbeatFailReportInterval = 180 * time.Second
+ //defaultGrpcTimeoutInterval is the time in seconds a grpc call will wait before returning error.
+ defaultGrpcTimeoutInterval = 2 * time.Second
+)
+
+// AdapterFlags represents the set of configurations used by the read-write adaptercore service
+type AdapterFlags struct {
+ // Command line parameters
+ InstanceID string
+ KafkaAdapterHost string
+ KafkaAdapterPort int
+ KafkaClusterHost string
+ KafkaClusterPort int
+ KVStoreType string
+ KVStoreTimeout int // in seconds
+ KVStoreHost string
+ KVStorePort int
+ Topic string
+ CoreTopic string
+ EventTopic string
+ LogLevel string
+ OnuNumber int
+ Banner bool
+ DisplayVersionOnly bool
+ ProbeHost string
+ ProbePort int
+ LiveProbeInterval time.Duration
+ NotLiveProbeInterval time.Duration
+ HeartbeatCheckInterval time.Duration
+ HeartbeatFailReportInterval time.Duration
+ GrpcTimeoutInterval time.Duration
+}
+
+func init() {
+ _, _ = log.AddPackage(log.JSON, log.WarnLevel, nil)
+}
+
+// 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,
+ ProbeHost: defaultProbeHost,
+ ProbePort: defaultProbePort,
+ LiveProbeInterval: defaultLiveProbeInterval,
+ NotLiveProbeInterval: defaultNotLiveProbeInterval,
+ HeartbeatCheckInterval: defaultHearbeatCheckInterval,
+ HeartbeatFailReportInterval: defaultHearbeatFailReportInterval,
+ GrpcTimeoutInterval: defaultGrpcTimeoutInterval,
+ }
+ 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 OLT 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.IntVar(&(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("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 seconds for GRPC timeout.")
+ flag.DurationVar(&(so.GrpcTimeoutInterval), "grpc_timeout_interval", defaultGrpcTimeoutInterval, help)
+
+ flag.Parse()
+ containerName := getContainerInfo()
+ if len(containerName) > 0 {
+ so.InstanceID = containerName
+ }
+
+}
+
+func getContainerInfo() string {
+ return os.Getenv("HOSTNAME")
+}
diff --git a/internal/pkg/config/config_test.go b/internal/pkg/config/config_test.go
new file mode 100644
index 0000000..332b9ea
--- /dev/null
+++ b/internal/pkg/config/config_test.go
@@ -0,0 +1,25 @@
+/*
+ * 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 "testing"
+
+func TestAdapterFlags_ParseCommandArguments(t *testing.T) {
+ so := NewAdapterFlags()
+ so.ParseCommandArguments()
+}
diff --git a/internal/pkg/core/device_handler.go b/internal/pkg/core/device_handler.go
new file mode 100644
index 0000000..0695af6
--- /dev/null
+++ b/internal/pkg/core/device_handler.go
@@ -0,0 +1,1828 @@
+/*
+ * 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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "context"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "google.golang.org/grpc/codes"
+
+ "github.com/cenkalti/backoff/v3"
+ "github.com/gogo/protobuf/proto"
+ "github.com/golang/protobuf/ptypes"
+ "github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "github.com/opencord/voltha-lib-go/v3/pkg/pmmetrics"
+ rsrcMgr "github.com/opencord/voltha-openolt-adapter/internal/pkg/resourcemanager"
+ "github.com/opencord/voltha-protos/v3/go/common"
+ ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+ of "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"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/status"
+)
+
+// Constants for number of retries and for timeout
+const (
+ MaxRetry = 10
+ MaxTimeOutInMs = 500
+)
+
+//DeviceHandler will interact with the OLT device.
+type DeviceHandler struct {
+ deviceID string
+ deviceType string
+ adminState string
+ device *voltha.Device
+ coreProxy adapterif.CoreProxy
+ AdapterProxy adapterif.AdapterProxy
+ EventProxy adapterif.EventProxy
+ openOLT *OpenOLT
+ exitChannel chan int
+ lockDevice sync.RWMutex
+ Client oop.OpenoltClient
+ transitionMap *TransitionMap
+ clientCon *grpc.ClientConn
+ flowMgr *OpenOltFlowMgr
+ eventMgr *OpenOltEventMgr
+ resourceMgr *rsrcMgr.OpenOltResourceMgr
+
+ discOnus sync.Map
+ onus sync.Map
+ portStats *OpenOltStatisticsMgr
+ metrics *pmmetrics.PmMetrics
+ stopCollector chan bool
+ stopHeartbeatCheck chan bool
+ activePorts sync.Map
+}
+
+//OnuDevice represents ONU related info
+type OnuDevice struct {
+ deviceID string
+ deviceType string
+ serialNumber string
+ onuID uint32
+ intfID uint32
+ proxyDeviceID string
+ uniPorts map[uint32]struct{}
+}
+
+var pmNames = []string{
+ "rx_bytes",
+ "rx_packets",
+ "rx_mcast_packets",
+ "rx_bcast_packets",
+ "tx_bytes",
+ "tx_packets",
+ "tx_mcast_packets",
+ "tx_bcast_packets",
+}
+
+//NewOnuDevice creates a new Onu Device
+func NewOnuDevice(devID, deviceTp, serialNum string, onuID, intfID uint32, proxyDevID string) *OnuDevice {
+ var device OnuDevice
+ device.deviceID = devID
+ device.deviceType = deviceTp
+ device.serialNumber = serialNum
+ device.onuID = onuID
+ device.intfID = intfID
+ device.proxyDeviceID = proxyDevID
+ device.uniPorts = make(map[uint32]struct{})
+ return &device
+}
+
+//NewDeviceHandler creates a new device handler
+func NewDeviceHandler(cp adapterif.CoreProxy, ap adapterif.AdapterProxy, ep adapterif.EventProxy, device *voltha.Device, adapter *OpenOLT) *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.openOLT = adapter
+ dh.exitChannel = make(chan int, 1)
+ dh.lockDevice = sync.RWMutex{}
+ dh.stopCollector = make(chan bool, 2)
+ dh.stopHeartbeatCheck = make(chan bool, 2)
+ dh.metrics = pmmetrics.NewPmMetrics(cloned.Id, pmmetrics.Frequency(150), pmmetrics.FrequencyOverride(false), pmmetrics.Grouped(false), pmmetrics.Metrics(pmNames))
+ dh.activePorts = sync.Map{}
+ //TODO initialize the support classes.
+ return &dh
+}
+
+// start save the device to the data model
+func (dh *DeviceHandler) start(ctx context.Context) {
+ dh.lockDevice.Lock()
+ defer dh.lockDevice.Unlock()
+ log.Debugw("starting-device-agent", log.Fields{"device": dh.device})
+ // Add the initial device to the local model
+ log.Debug("device-agent-started")
+}
+
+// stop stops the device dh. Not much to do for now
+func (dh *DeviceHandler) stop(ctx context.Context) {
+ dh.lockDevice.Lock()
+ defer dh.lockDevice.Unlock()
+ log.Debug("stopping-device-agent")
+ dh.exitChannel <- 1
+ log.Debug("device-agent-stopped")
+}
+
+func macifyIP(ip net.IP) string {
+ if len(ip) > 0 {
+ oct1 := strconv.FormatInt(int64(ip[12]), 16)
+ oct2 := strconv.FormatInt(int64(ip[13]), 16)
+ oct3 := strconv.FormatInt(int64(ip[14]), 16)
+ oct4 := strconv.FormatInt(int64(ip[15]), 16)
+ return fmt.Sprintf("00:00:%02v:%02v:%02v:%02v", oct1, oct2, oct3, oct4)
+ }
+ return ""
+}
+
+func generateMacFromHost(host string) (string, error) {
+ var genmac string
+ var addr net.IP
+ var ips []string
+ var err error
+
+ log.Debugw("generating-mac-from-host", log.Fields{"host": host})
+
+ if addr = net.ParseIP(host); addr == nil {
+ log.Debugw("looking-up-hostname", log.Fields{"host": host})
+
+ if ips, err = net.LookupHost(host); err == nil {
+ log.Debugw("dns-result-ips", log.Fields{"ips": ips})
+ if addr = net.ParseIP(ips[0]); addr == nil {
+ return "", NewErrInvalidValue(log.Fields{"ip": ips[0]}, nil).Log()
+ }
+ genmac = macifyIP(addr)
+ log.Debugw("using-ip-as-mac", log.Fields{"host": ips[0], "mac": genmac})
+ return genmac, nil
+ }
+ return "", NewErrAdapter("cannot-resolve-hostname-to-ip", nil, err).Log()
+ }
+
+ genmac = macifyIP(addr)
+ log.Debugw("using-ip-as-mac", log.Fields{"host": host, "mac": genmac})
+ return genmac, nil
+}
+
+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
+}
+
+//GetportLabel returns the label for the NNI and the PON port based on port number and port type
+func GetportLabel(portNum uint32, portType voltha.Port_PortType) (string, error) {
+
+ switch portType {
+ case voltha.Port_ETHERNET_NNI:
+ return fmt.Sprintf("nni-%d", portNum), nil
+ case voltha.Port_PON_OLT:
+ return fmt.Sprintf("pon-%d", portNum), nil
+ }
+
+ return "", NewErrInvalidValue(log.Fields{"port-type": portType}, nil).Log()
+}
+
+func (dh *DeviceHandler) addPort(intfID uint32, portType voltha.Port_PortType, state string) error {
+ var operStatus common.OperStatus_Types
+ if state == "up" {
+ operStatus = voltha.OperStatus_ACTIVE
+ //populating the intfStatus map
+ dh.activePorts.Store(intfID, true)
+ } else {
+ operStatus = voltha.OperStatus_DISCOVERED
+ dh.activePorts.Store(intfID, false)
+ }
+ portNum := IntfIDToPortNo(intfID, portType)
+ label, err := GetportLabel(portNum, portType)
+ if err != nil {
+ return NewErrNotFound("port-label", log.Fields{"port-number": portNum, "port-type": portType}, nil).Log()
+ }
+
+ device, err := dh.coreProxy.GetDevice(context.TODO(), dh.device.Id, dh.device.Id)
+ if err != nil || device == nil {
+ return NewErrNotFound("device", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ if device.Ports != nil {
+ for _, dPort := range device.Ports {
+ if dPort.Type == portType && dPort.PortNo == portNum {
+ log.Debug("port-already-exists-updating-oper-status-of-port")
+ if err := dh.coreProxy.PortStateUpdate(context.TODO(), dh.device.Id, portType, portNum, operStatus); err != nil {
+ return NewErrAdapter("failed-to-update-port-state", log.Fields{
+ "device-id": dh.device.Id,
+ "port-type": portType,
+ "port-number": portNum,
+ "oper-status": operStatus}, err).Log()
+
+ }
+ return nil
+ }
+ }
+ }
+ // Now create Port
+ port := &voltha.Port{
+ PortNo: portNum,
+ Label: label,
+ Type: portType,
+ OperStatus: operStatus,
+ }
+ log.Debugw("Sending-port-update-to-core", log.Fields{"port": port})
+ // Synchronous call to update device - this method is run in its own go routine
+ if err := dh.coreProxy.PortCreated(context.TODO(), dh.device.Id, port); err != nil {
+ return NewErrAdapter("Error-creating-port", log.Fields{
+ "device-id": dh.device.Id,
+ "port-type": portType}, err).Log()
+ }
+ return nil
+}
+
+// readIndications to read the indications from the OLT device
+func (dh *DeviceHandler) readIndications(ctx context.Context) error {
+ defer log.Debugw("indications-ended", log.Fields{"device-id": dh.device.Id})
+ indications, err := dh.Client.EnableIndication(ctx, new(oop.Empty))
+ if err != nil {
+ return NewErrCommunication("fail-to-read-indications", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ if indications == nil {
+ return NewErrInvalidValue(log.Fields{"indications": nil, "device-id": dh.device.Id}, nil).Log()
+ }
+ /* get device state */
+ device, err := dh.coreProxy.GetDevice(ctx, dh.device.Id, dh.device.Id)
+ if err != nil || device == nil {
+ /*TODO: needs to handle error scenarios */
+ return NewErrNotFound("device", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ // When the device is in DISABLED and Adapter container restarts, we need to
+ // rebuild the locally maintained admin state.
+ if device.AdminState == voltha.AdminState_DISABLED {
+ dh.lockDevice.Lock()
+ dh.adminState = "down"
+ dh.lockDevice.Unlock()
+ }
+
+ // Create an exponential backoff around re-enabling indications. The
+ // maximum elapsed time for the back off is set to 0 so that we will
+ // continue to retry. The max interval defaults to 1m, but is set
+ // here for code clarity
+ indicationBackoff := backoff.NewExponentialBackOff()
+ indicationBackoff.MaxElapsedTime = 0
+ indicationBackoff.MaxInterval = 1 * time.Minute
+ for {
+ indication, err := indications.Recv()
+ if err == io.EOF {
+ log.Infow("EOF for indications", log.Fields{"err": err})
+ // Use an exponential back off to prevent getting into a tight loop
+ duration := indicationBackoff.NextBackOff()
+ if duration == backoff.Stop {
+ // If we reach a maximum then warn and reset the backoff
+ // timer and keep attempting.
+ log.Warnw("Maximum indication backoff reached, resetting backoff timer",
+ log.Fields{"max_indication_backoff": indicationBackoff.MaxElapsedTime})
+ indicationBackoff.Reset()
+ }
+ time.Sleep(indicationBackoff.NextBackOff())
+ indications, err = dh.Client.EnableIndication(ctx, new(oop.Empty))
+ if err != nil {
+ return NewErrCommunication("indication-read-failure", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ continue
+ }
+ if err != nil {
+ log.Infow("Failed to read from indications", log.Fields{"err": err})
+ if dh.adminState == "deleted" {
+ log.Debug("Device deleted stoping the read indication thread")
+ break
+ }
+ dh.transitionMap.Handle(ctx, DeviceDownInd)
+ dh.transitionMap.Handle(ctx, DeviceInit)
+ return NewErrCommunication("indication-read-failure", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ // Reset backoff if we have a successful receive
+ indicationBackoff.Reset()
+ dh.lockDevice.RLock()
+ adminState := dh.adminState
+ dh.lockDevice.RUnlock()
+ // When OLT is admin down, ignore all indications.
+ if adminState == "down" {
+
+ log.Infow("olt is admin down, ignore indication", log.Fields{"indication": indication})
+ continue
+ }
+ dh.handleIndication(ctx, indication)
+
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) handleOltIndication(ctx context.Context, oltIndication *oop.OltIndication) error {
+ raisedTs := time.Now().UnixNano()
+ if oltIndication.OperState == "up" && dh.transitionMap.currentDeviceState != deviceStateUp {
+ dh.transitionMap.Handle(ctx, DeviceUpInd)
+ } else if oltIndication.OperState == "down" {
+ dh.transitionMap.Handle(ctx, DeviceDownInd)
+ }
+ // Send or clear Alarm
+ if err := dh.eventMgr.oltUpDownIndication(oltIndication, dh.deviceID, raisedTs); err != nil {
+ return NewErrAdapter("failed-indication", log.Fields{
+ "device_id": dh.deviceID,
+ "indication": oltIndication,
+ "timestamp": raisedTs}, err).Log()
+ }
+ return nil
+}
+
+// nolint: gocyclo
+func (dh *DeviceHandler) handleIndication(ctx context.Context, indication *oop.Indication) {
+ raisedTs := time.Now().UnixNano()
+ switch indication.Data.(type) {
+ case *oop.Indication_OltInd:
+ if err := dh.handleOltIndication(ctx, indication.GetOltInd()); err != nil {
+ NewErrAdapter("handle-indication-error", log.Fields{"type": "olt"}, err).Log()
+ }
+ case *oop.Indication_IntfInd:
+ intfInd := indication.GetIntfInd()
+ go func() {
+ if err := dh.addPort(intfInd.GetIntfId(), voltha.Port_PON_OLT, intfInd.GetOperState()); err != nil {
+ NewErrAdapter("handle-indication-error", log.Fields{"type": "interface"}, err).Log()
+ }
+ }()
+ log.Infow("Received interface indication ", log.Fields{"InterfaceInd": intfInd})
+ case *oop.Indication_IntfOperInd:
+ intfOperInd := indication.GetIntfOperInd()
+ if intfOperInd.GetType() == "nni" {
+ go func() {
+ if err := dh.addPort(intfOperInd.GetIntfId(), voltha.Port_ETHERNET_NNI, intfOperInd.GetOperState()); err != nil {
+ NewErrAdapter("handle-indication-error", log.Fields{"type": "interface-oper-nni"}, err).Log()
+ }
+ }()
+ dh.resourceMgr.AddNNIToKVStore(ctx, intfOperInd.GetIntfId())
+ } else if intfOperInd.GetType() == "pon" {
+ // TODO: Check what needs to be handled here for When PON PORT down, ONU will be down
+ // Handle pon port update
+ go func() {
+ if err := dh.addPort(intfOperInd.GetIntfId(), voltha.Port_PON_OLT, intfOperInd.GetOperState()); err != nil {
+ NewErrAdapter("handle-indication-error", log.Fields{"type": "interface-oper-pon"}, err).Log()
+ }
+ }()
+ go dh.eventMgr.oltIntfOperIndication(indication.GetIntfOperInd(), dh.deviceID, raisedTs)
+ }
+ log.Infow("Received interface oper indication ", log.Fields{"InterfaceOperInd": intfOperInd})
+ case *oop.Indication_OnuDiscInd:
+ onuDiscInd := indication.GetOnuDiscInd()
+ log.Infow("Received Onu discovery indication ", log.Fields{"OnuDiscInd": onuDiscInd})
+ sn := dh.stringifySerialNumber(onuDiscInd.SerialNumber)
+ go func() {
+ if err := dh.onuDiscIndication(ctx, onuDiscInd, sn); err != nil {
+ NewErrAdapter("handle-indication-error", log.Fields{"type": "onu-discovery"}, err).Log()
+ }
+ }()
+ case *oop.Indication_OnuInd:
+ onuInd := indication.GetOnuInd()
+ log.Infow("Received Onu indication ", log.Fields{"OnuInd": onuInd})
+ go func() {
+ if err := dh.onuIndication(onuInd); err != nil {
+ NewErrAdapter("handle-indication-error", log.Fields{"type": "onu"}, err).Log()
+ }
+ }()
+ case *oop.Indication_OmciInd:
+ omciInd := indication.GetOmciInd()
+ log.Debugw("Received Omci indication ", log.Fields{"IntfId": omciInd.IntfId, "OnuId": omciInd.OnuId, "pkt": hex.EncodeToString(omciInd.Pkt)})
+ go func() {
+ if err := dh.omciIndication(omciInd); err != nil {
+ NewErrAdapter("handle-indication-error", log.Fields{"type": "omci"}, err).Log()
+ }
+ }()
+ case *oop.Indication_PktInd:
+ pktInd := indication.GetPktInd()
+ log.Infow("Received pakcet indication ", log.Fields{"PktInd": pktInd})
+ go func() {
+ if err := dh.handlePacketIndication(ctx, pktInd); err != nil {
+ NewErrAdapter("handle-indication-error", log.Fields{"type": "packet"}, err).Log()
+ }
+ }()
+ case *oop.Indication_PortStats:
+ portStats := indication.GetPortStats()
+ go dh.portStats.PortStatisticsIndication(portStats, dh.resourceMgr.DevInfo.GetPonPorts())
+ case *oop.Indication_FlowStats:
+ flowStats := indication.GetFlowStats()
+ log.Infow("Received flow stats", log.Fields{"FlowStats": flowStats})
+ case *oop.Indication_AlarmInd:
+ alarmInd := indication.GetAlarmInd()
+ log.Infow("Received alarm indication ", log.Fields{"AlarmInd": alarmInd})
+ go dh.eventMgr.ProcessEvents(alarmInd, dh.deviceID, raisedTs)
+ }
+}
+
+// doStateUp handle the olt up indication and update to voltha core
+func (dh *DeviceHandler) doStateUp(ctx context.Context) error {
+ // 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 {
+ return NewErrAdapter("device-update-failed", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ return nil
+}
+
+// doStateDown handle the olt down indication
+func (dh *DeviceHandler) doStateDown(ctx context.Context) error {
+ dh.lockDevice.Lock()
+ defer dh.lockDevice.Unlock()
+ log.Debug("do-state-down-start")
+
+ device, err := dh.coreProxy.GetDevice(ctx, dh.device.Id, dh.device.Id)
+ if err != nil || device == nil {
+ /*TODO: needs to handle error scenarios */
+ return NewErrNotFound("device", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+
+ cloned := proto.Clone(device).(*voltha.Device)
+ // Update the all ports state on that device to disable
+ if err = dh.coreProxy.PortsStateUpdate(ctx, cloned.Id, voltha.OperStatus_UNKNOWN); err != nil {
+ return NewErrAdapter("port-update-failed", log.Fields{"device-id": device.Id}, err).Log()
+ }
+
+ //Update the device oper state and connection status
+ cloned.OperStatus = voltha.OperStatus_UNKNOWN
+ cloned.ConnectStatus = common.ConnectStatus_UNREACHABLE
+ dh.device = cloned
+
+ if err = dh.coreProxy.DeviceStateUpdate(ctx, cloned.Id, cloned.ConnectStatus, cloned.OperStatus); err != nil {
+ return NewErrAdapter("state-update-failed", log.Fields{"device-id": device.Id}, err).Log()
+ }
+
+ //get the child device for the parent device
+ onuDevices, err := dh.coreProxy.GetChildDevices(ctx, dh.device.Id)
+ if err != nil {
+ return NewErrAdapter("child-device-fetch-failed", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ for _, onuDevice := range onuDevices.Items {
+
+ // Update onu state as down in onu adapter
+ onuInd := oop.OnuIndication{}
+ onuInd.OperState = "down"
+ err := dh.AdapterProxy.SendInterAdapterMessage(ctx, &onuInd, ic.InterAdapterMessageType_ONU_IND_REQUEST,
+ "openolt", onuDevice.Type, onuDevice.Id, onuDevice.ProxyAddress.DeviceId, "")
+ if err != nil {
+ NewErrCommunication("inter-adapter-send-failed", log.Fields{
+ "source": "openolt",
+ "onu-indicator": onuInd,
+ "device-type": onuDevice.Type,
+ "device-id": onuDevice.Id}, err).LogAt(log.ErrorLevel)
+ //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{}
+ log.Debugw("do-state-down-end", log.Fields{"device-id": device.Id})
+ return nil
+}
+
+// doStateInit dial the grpc before going to init state
+func (dh *DeviceHandler) doStateInit(ctx context.Context) error {
+ var err error
+ if dh.clientCon, err = grpc.Dial(dh.device.GetHostAndPort(), grpc.WithInsecure(), grpc.WithBlock()); err != nil {
+ return NewErrCommunication("dial-failure", log.Fields{
+ "device-id": dh.deviceID,
+ "host-and-port": dh.device.GetHostAndPort()}, err).Log()
+ }
+ return nil
+}
+
+// postInit create olt client instance to invoke RPC on the olt device
+func (dh *DeviceHandler) postInit(ctx context.Context) error {
+ dh.Client = oop.NewOpenoltClient(dh.clientCon)
+ dh.transitionMap.Handle(ctx, GrpcConnected)
+ return nil
+}
+
+// doStateConnected get the device info and update to voltha core
+func (dh *DeviceHandler) doStateConnected(ctx context.Context) error {
+ log.Debug("OLT device has been connected")
+
+ // Case where OLT is disabled and then rebooted.
+ if dh.adminState == "down" {
+ log.Debugln("do-state-connected--device-admin-state-down")
+ device, err := dh.coreProxy.GetDevice(ctx, dh.device.Id, dh.device.Id)
+ if err != nil || device == nil {
+ /*TODO: needs to handle error scenarios */
+ NewErrAdapter("device-fetch-failed", log.Fields{"device-id": dh.device.Id}, err).LogAt(log.ErrorLevel)
+ }
+
+ cloned := proto.Clone(device).(*voltha.Device)
+ cloned.ConnectStatus = voltha.ConnectStatus_REACHABLE
+ cloned.OperStatus = voltha.OperStatus_UNKNOWN
+ dh.device = cloned
+ if er := dh.coreProxy.DeviceStateUpdate(ctx, cloned.Id, cloned.ConnectStatus, cloned.OperStatus); er != nil {
+ NewErrAdapter("device-state-update-failed", log.Fields{"device-id": dh.device.Id}, err).LogAt(log.ErrorLevel)
+ }
+
+ // Since the device was disabled before the OLT was rebooted, enforce the OLT to be Disabled after re-connection.
+ _, err = dh.Client.DisableOlt(ctx, new(oop.Empty))
+ if err != nil {
+ NewErrAdapter("olt-disable-failed", log.Fields{"device-id": dh.device.Id}, err).LogAt(log.ErrorLevel)
+ }
+
+ // Start reading indications
+ go func() {
+ if err := dh.readIndications(ctx); err != nil {
+ NewErrAdapter("indication-read-failure", log.Fields{"device-id": dh.device.Id}, err).LogAt(log.ErrorLevel)
+ }
+ }()
+ return nil
+ }
+
+ deviceInfo, err := dh.populateDeviceInfo()
+ if err != nil {
+ return NewErrAdapter("populate-device-info-failed", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+
+ device, err := dh.coreProxy.GetDevice(context.TODO(), dh.device.Id, dh.device.Id)
+ if err != nil || device == nil {
+ /*TODO: needs to handle error scenarios */
+ return NewErrAdapter("fetch-device-failed", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ dh.populateActivePorts(device)
+ if err := dh.disableAdminDownPorts(device); err != nil {
+ return NewErrAdapter("port-status-update-failed", log.Fields{"device": device}, err).Log()
+ }
+
+ KVStoreHostPort := fmt.Sprintf("%s:%d", dh.openOLT.KVStoreHost, dh.openOLT.KVStorePort)
+ // Instantiate resource manager
+ if dh.resourceMgr = rsrcMgr.NewResourceMgr(ctx, dh.deviceID, KVStoreHostPort, dh.openOLT.KVStoreType, dh.deviceType, deviceInfo); dh.resourceMgr == nil {
+ return ErrResourceManagerInstantiating.Log()
+ }
+ // Instantiate flow manager
+ if dh.flowMgr = NewFlowManager(ctx, dh, dh.resourceMgr); dh.flowMgr == nil {
+ return ErrResourceManagerInstantiating.Log()
+ }
+ /* TODO: Instantiate Alarm , stats , BW managers */
+ /* Instantiating Event Manager to handle Alarms and KPIs */
+ dh.eventMgr = NewEventMgr(dh.EventProxy, dh)
+ // Stats config for new device
+ dh.portStats = NewOpenOltStatsMgr(dh)
+
+ // Start reading indications
+ go func() {
+ if err := dh.readIndications(ctx); err != nil {
+ NewErrAdapter("read-indications-failure", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ }()
+ return nil
+}
+
+func (dh *DeviceHandler) populateDeviceInfo() (*oop.DeviceInfo, error) {
+ var err error
+ var deviceInfo *oop.DeviceInfo
+
+ deviceInfo, err = dh.Client.GetDeviceInfo(context.Background(), new(oop.Empty))
+
+ if err != nil {
+ return nil, NewErrPersistence("get", "device", 0, nil, err).Log()
+ }
+ if deviceInfo == nil {
+ return nil, NewErrInvalidValue(log.Fields{"device": nil}, nil).Log()
+ }
+
+ log.Debugw("Fetched device info", log.Fields{"deviceInfo": deviceInfo})
+ dh.device.Root = true
+ dh.device.Vendor = deviceInfo.Vendor
+ dh.device.Model = deviceInfo.Model
+ dh.device.SerialNumber = deviceInfo.DeviceSerialNumber
+ dh.device.HardwareVersion = deviceInfo.HardwareVersion
+ dh.device.FirmwareVersion = deviceInfo.FirmwareVersion
+
+ if deviceInfo.DeviceId == "" {
+ log.Warnw("no-device-id-provided-using-host", log.Fields{"hostport": dh.device.GetHostAndPort()})
+ host := strings.Split(dh.device.GetHostAndPort(), ":")[0]
+ genmac, err := generateMacFromHost(host)
+ if err != nil {
+ return nil, NewErrAdapter("failed-to-generate-mac-host", log.Fields{"host": host}, err).Log()
+ }
+ log.Debugw("using-host-for-mac-address", log.Fields{"host": host, "mac": genmac})
+ dh.device.MacAddress = genmac
+ } else {
+ dh.device.MacAddress = deviceInfo.DeviceId
+ }
+
+ // Synchronous call to update device - this method is run in its own go routine
+ if err := dh.coreProxy.DeviceUpdate(context.TODO(), dh.device); err != nil {
+ return nil, NewErrAdapter("device-update-failed", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+
+ return deviceInfo, nil
+}
+
+func startCollector(dh *DeviceHandler) {
+ // Initial delay for OLT initialization
+ time.Sleep(1 * time.Minute)
+ log.Debugf("Starting-Collector")
+ context := make(map[string]string)
+ for {
+ select {
+ case <-dh.stopCollector:
+ log.Debugw("Stopping-Collector-for-OLT", log.Fields{"deviceID:": dh.deviceID})
+ return
+ default:
+ freq := dh.metrics.ToPmConfigs().DefaultFreq
+ time.Sleep(time.Duration(freq) * time.Second)
+ context["oltid"] = dh.deviceID
+ context["devicetype"] = dh.deviceType
+ // NNI Stats
+ cmnni := dh.portStats.collectNNIMetrics(uint32(0))
+ log.Debugf("Collect-NNI-Metrics %v", cmnni)
+ go dh.portStats.publishMetrics("NNIStats", cmnni, uint32(0), context, dh.deviceID)
+ log.Debugf("Publish-NNI-Metrics")
+ // PON Stats
+ NumPonPORTS := dh.resourceMgr.DevInfo.GetPonPorts()
+ for i := uint32(0); i < NumPonPORTS; i++ {
+ if val, ok := dh.activePorts.Load(i); ok && val == true {
+ cmpon := dh.portStats.collectPONMetrics(i)
+ log.Debugf("Collect-PON-Metrics %v", cmpon)
+
+ go dh.portStats.publishMetrics("PONStats", cmpon, i, context, dh.deviceID)
+ log.Debugf("Publish-PON-Metrics")
+ }
+ }
+ }
+ }
+}
+
+//AdoptDevice adopts the OLT device
+func (dh *DeviceHandler) AdoptDevice(ctx context.Context, device *voltha.Device) {
+ dh.transitionMap = NewTransitionMap(dh)
+ log.Infow("Adopt_device", log.Fields{"deviceID": device.Id, "Address": device.GetHostAndPort()})
+ dh.transitionMap.Handle(ctx, DeviceInit)
+
+ // Now, set the initial PM configuration for that device
+ if err := dh.coreProxy.DevicePMConfigUpdate(nil, dh.metrics.ToPmConfigs()); err != nil {
+ NewErrAdapter("error-updating-performance-metrics", log.Fields{"device-id": device.Id}, err).LogAt(log.ErrorLevel)
+ }
+
+ go startCollector(dh)
+ go startHeartbeatCheck(dh)
+}
+
+//GetOfpDeviceInfo Gets the Ofp information of the given device
+func (dh *DeviceHandler) GetOfpDeviceInfo(device *voltha.Device) (*ic.SwitchCapability, error) {
+ return &ic.SwitchCapability{
+ Desc: &of.OfpDesc{
+ MfrDesc: "VOLTHA Project",
+ HwDesc: "open_pon",
+ SwDesc: "open_pon",
+ SerialNum: dh.device.SerialNumber,
+ },
+ SwitchFeatures: &of.OfpSwitchFeatures{
+ NBuffers: 256,
+ NTables: 2,
+ Capabilities: uint32(of.OfpCapabilities_OFPC_FLOW_STATS |
+ of.OfpCapabilities_OFPC_TABLE_STATS |
+ of.OfpCapabilities_OFPC_PORT_STATS |
+ of.OfpCapabilities_OFPC_GROUP_STATS),
+ },
+ }, nil
+}
+
+//GetOfpPortInfo Get Ofp port information
+func (dh *DeviceHandler) GetOfpPortInfo(device *voltha.Device, portNo int64) (*ic.PortCapability, error) {
+ capacity := uint32(of.OfpPortFeatures_OFPPF_1GB_FD | of.OfpPortFeatures_OFPPF_FIBER)
+ return &ic.PortCapability{
+ Port: &voltha.LogicalPort{
+ OfpPort: &of.OfpPort{
+ HwAddr: macAddressToUint32Array(dh.device.MacAddress),
+ Config: 0,
+ State: uint32(of.OfpPortState_OFPPS_LIVE),
+ Curr: capacity,
+ Advertised: capacity,
+ Peer: capacity,
+ CurrSpeed: uint32(of.OfpPortFeatures_OFPPF_1GB_FD),
+ MaxSpeed: uint32(of.OfpPortFeatures_OFPPF_1GB_FD),
+ },
+ DeviceId: dh.device.Id,
+ DevicePortNo: uint32(portNo),
+ },
+ }, nil
+}
+
+func (dh *DeviceHandler) omciIndication(omciInd *oop.OmciIndication) error {
+ log.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 {
+
+ log.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 {
+ return NewErrNotFound("onu", log.Fields{
+ "interface-id": omciInd.IntfId,
+ "onu-id": omciInd.OnuId}, err).Log()
+ }
+ 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
+ log.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
+ }
+
+ omciMsg := &ic.InterAdapterOmciMessage{Message: omciInd.Pkt}
+ if err := dh.AdapterProxy.SendInterAdapterMessage(context.Background(), omciMsg,
+ ic.InterAdapterMessageType_OMCI_REQUEST, dh.deviceType, deviceType,
+ deviceID, proxyDeviceID, ""); err != nil {
+ return NewErrCommunication("omci-request", log.Fields{
+ "source": dh.deviceType,
+ "destination": deviceType,
+ "onu-id": deviceID,
+ "proxy-device-id": proxyDeviceID}, err).Log()
+ }
+ 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 {
+ log.Debugw("Process_inter_adapter_message", log.Fields{"msgID": msg.Header.Id})
+ if msg.Header.Type == ic.InterAdapterMessageType_OMCI_REQUEST {
+ msgID := msg.Header.Id
+ fromTopic := msg.Header.FromTopic
+ toTopic := msg.Header.ToTopic
+ toDeviceID := msg.Header.ToDeviceId
+ proxyDeviceID := msg.Header.ProxyDeviceId
+
+ log.Debugw("omci request message header", log.Fields{"msgID": msgID, "fromTopic": fromTopic, "toTopic": toTopic, "toDeviceID": toDeviceID, "proxyDeviceID": proxyDeviceID})
+
+ msgBody := msg.GetBody()
+
+ omciMsg := &ic.InterAdapterOmciMessage{}
+ if err := ptypes.UnmarshalAny(msgBody, omciMsg); err != nil {
+ log.Warnw("cannot-unmarshal-omci-msg-body", log.Fields{"error": err})
+ return err
+ }
+
+ if omciMsg.GetProxyAddress() == nil {
+ onuDevice, err := dh.coreProxy.GetDevice(context.TODO(), dh.device.Id, toDeviceID)
+ if err != nil {
+ return NewErrNotFound("onu", log.Fields{
+ "device-id": dh.device.Id,
+ "onu-device-id": toDeviceID}, err).Log()
+ }
+ log.Debugw("device retrieved from core", log.Fields{"msgID": msgID, "fromTopic": fromTopic, "toTopic": toTopic, "toDeviceID": toDeviceID, "proxyDeviceID": proxyDeviceID})
+ if err := dh.sendProxiedMessage(onuDevice, omciMsg); err != nil {
+ return NewErrCommunication("send-failed", log.Fields{
+ "device-id": dh.device.Id,
+ "onu-device-id": toDeviceID}, err).Log()
+ }
+ } else {
+ log.Debugw("Proxy Address found in omci message", log.Fields{"msgID": msgID, "fromTopic": fromTopic, "toTopic": toTopic, "toDeviceID": toDeviceID, "proxyDeviceID": proxyDeviceID})
+ if err := dh.sendProxiedMessage(nil, omciMsg); err != nil {
+ return NewErrCommunication("send-failed", log.Fields{
+ "device-id": dh.device.Id,
+ "onu-device-id": toDeviceID}, err).Log()
+ }
+ }
+
+ } else {
+ return NewErrInvalidValue(log.Fields{"inter-adapter-message-type": msg.Header.Type}, nil).Log()
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) sendProxiedMessage(onuDevice *voltha.Device, omciMsg *ic.InterAdapterOmciMessage) error {
+ var intfID uint32
+ var onuID uint32
+ var connectStatus common.ConnectStatus_Types
+ if onuDevice != nil {
+ intfID = onuDevice.ProxyAddress.GetChannelId()
+ onuID = onuDevice.ProxyAddress.GetOnuId()
+ connectStatus = onuDevice.ConnectStatus
+ } else {
+ intfID = omciMsg.GetProxyAddress().GetChannelId()
+ onuID = omciMsg.GetProxyAddress().GetOnuId()
+ connectStatus = omciMsg.GetConnectStatus()
+ }
+ if connectStatus != voltha.ConnectStatus_REACHABLE {
+ log.Debugw("ONU is not reachable, cannot send OMCI", log.Fields{"intfID": intfID, "onuID": onuID})
+
+ return NewErrCommunication("unreachable", log.Fields{
+ "interface-id": intfID,
+ "onu-id": onuID}, nil).Log()
+ }
+
+ // TODO: Once we are sure openonu/openomci is sending only binary in omciMsg.Message, we can remove this check
+ isHexString := false
+ _, decodeerr := hex.DecodeString(string(omciMsg.Message))
+ if decodeerr == nil {
+ isHexString = true
+ }
+
+ // TODO: OpenOLT Agent expects a hex string for OMCI packets rather than binary. Fix this in the agent and then we can pass binary Pkt: omciMsg.Message.
+ var omciMessage *oop.OmciMsg
+ if isHexString {
+ omciMessage = &oop.OmciMsg{IntfId: intfID, OnuId: onuID, Pkt: omciMsg.Message}
+ } else {
+ hexPkt := make([]byte, hex.EncodedLen(len(omciMsg.Message)))
+ hex.Encode(hexPkt, omciMsg.Message)
+ omciMessage = &oop.OmciMsg{IntfId: intfID, OnuId: onuID, Pkt: hexPkt}
+ }
+
+ _, err := dh.Client.OmciMsgOut(context.Background(), omciMessage)
+ if err != nil {
+ return NewErrCommunication("omci-send-failed", log.Fields{
+ "interface-id": intfID,
+ "onu-id": onuID,
+ "message": omciMessage}, err).Log()
+ }
+ log.Debugw("Sent Omci message", log.Fields{"intfID": intfID, "onuID": onuID, "omciMsg": hex.EncodeToString(omciMsg.Message)})
+ return nil
+}
+
+func (dh *DeviceHandler) activateONU(ctx context.Context, intfID uint32, onuID int64, serialNum *oop.SerialNumber, serialNumber string) error {
+ log.Debugw("activate-onu", log.Fields{"intfID": intfID, "onuID": onuID, "serialNum": serialNum, "serialNumber": serialNumber})
+ dh.flowMgr.UpdateOnuInfo(ctx, intfID, uint32(onuID), serialNumber)
+ // TODO: need resource manager
+ var pir uint32 = 1000000
+ Onu := oop.Onu{IntfId: intfID, OnuId: uint32(onuID), SerialNumber: serialNum, Pir: pir}
+ if _, err := dh.Client.ActivateOnu(ctx, &Onu); err != nil {
+ st, _ := status.FromError(err)
+ if st.Code() == codes.AlreadyExists {
+ log.Debug("ONU activation is in progress", log.Fields{"SerialNumber": serialNumber})
+ } else {
+ return NewErrAdapter("onu-activate-failed", log.Fields{"onu": Onu}, err).Log()
+ }
+ } else {
+ log.Infow("activated-onu", log.Fields{"SerialNumber": serialNumber})
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) onuDiscIndication(ctx context.Context, onuDiscInd *oop.OnuDiscIndication, sn string) error {
+
+ channelID := onuDiscInd.GetIntfId()
+ parentPortNo := IntfIDToPortNo(onuDiscInd.GetIntfId(), voltha.Port_PON_OLT)
+
+ log.Infow("new-discovery-indication", log.Fields{"sn": sn})
+
+ kwargs := make(map[string]interface{})
+ if sn != "" {
+ kwargs["serial_number"] = sn
+ } else {
+ return NewErrInvalidValue(log.Fields{"serial-number": sn}, nil).Log()
+ }
+
+ if _, loaded := dh.discOnus.LoadOrStore(sn, true); loaded {
+ log.Warnw("onu-sn-is-already-being-processed", log.Fields{"sn": sn})
+ return nil
+ }
+
+ var onuID uint32
+
+ // check the ONU is already know to the OLT
+ // NOTE the second time the ONU is discovered this should return a device
+ onuDevice, err := dh.coreProxy.GetChildDevice(ctx, dh.device.Id, kwargs)
+
+ if err != nil {
+ log.Warnw("core-proxy-get-child-device-failed", log.Fields{"parentDevice": dh.device.Id, "err": err, "sn": sn})
+ if e, ok := status.FromError(err); ok {
+ log.Warnw("core-proxy-get-child-device-failed-with-code", log.Fields{"errCode": e.Code(), "sn": sn})
+ switch e.Code() {
+ case codes.Internal:
+ // this probably means NOT FOUND, so just create a new device
+ onuDevice = nil
+ case codes.DeadlineExceeded:
+ // if the call times out, cleanup and exit
+ dh.discOnus.Delete(sn)
+ return NewErrTimeout("get-child-device", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ }
+ }
+
+ if onuDevice == nil {
+ // NOTE this should happen a single time, and only if GetChildDevice returns NotFound
+ log.Infow("creating-new-onu", log.Fields{"sn": sn})
+ // we need to create a new ChildDevice
+ ponintfid := onuDiscInd.GetIntfId()
+ dh.lockDevice.Lock()
+ onuID, err = dh.resourceMgr.GetONUID(ctx, ponintfid)
+ dh.lockDevice.Unlock()
+
+ log.Infow("creating-new-onu-got-onu-id", log.Fields{"sn": sn, "onuId": onuID})
+
+ if err != nil {
+ // if we can't create an ID in resource manager,
+ // cleanup and exit
+ dh.discOnus.Delete(sn)
+ return NewErrAdapter("resource-manage-get-onu-id-failed", log.Fields{
+ "pon-interface-id": ponintfid,
+ "serial-number": sn}, err).Log()
+ }
+
+ if onuDevice, err = dh.coreProxy.ChildDeviceDetected(context.TODO(), dh.device.Id, int(parentPortNo),
+ "", int(channelID), string(onuDiscInd.SerialNumber.GetVendorId()), sn, int64(onuID)); err != nil {
+ dh.discOnus.Delete(sn)
+ dh.resourceMgr.FreeonuID(ctx, ponintfid, []uint32{onuID}) // NOTE I'm not sure this method is actually cleaning up the right thing
+ return NewErrAdapter("core-proxy-child-device-detected-failed", log.Fields{
+ "pon-interface-id": ponintfid,
+ "serial-number": sn}, err).Log()
+ }
+
+ log.Infow("onu-child-device-added", log.Fields{"onuDevice": onuDevice, "sn": sn})
+ }
+
+ // we can now use the existing ONU Id
+ onuID = onuDevice.ProxyAddress.OnuId
+
+ //Insert the ONU into cache to use in OnuIndication.
+ //TODO: Do we need to remove this from the cache on ONU change, or wait for overwritten on next discovery.
+ log.Debugw("onu-discovery-indication-key-create", log.Fields{"onuID": onuID,
+ "intfId": onuDiscInd.GetIntfId(), "sn": sn})
+ onuKey := dh.formOnuKey(onuDiscInd.GetIntfId(), onuID)
+
+ onuDev := NewOnuDevice(onuDevice.Id, onuDevice.Type, onuDevice.SerialNumber, onuID, onuDiscInd.GetIntfId(), onuDevice.ProxyAddress.DeviceId)
+ dh.onus.Store(onuKey, onuDev)
+ log.Debugw("new-onu-device-discovered", log.Fields{"onu": onuDev, "sn": sn})
+
+ if err = dh.coreProxy.DeviceStateUpdate(ctx, onuDevice.Id, common.ConnectStatus_REACHABLE, common.OperStatus_DISCOVERED); err != nil {
+ return NewErrAdapter("failed-to-update-device-state", log.Fields{
+ "device-id": onuDevice.Id,
+ "serial-number": sn}, err).Log()
+ }
+ log.Infow("onu-discovered-reachable", log.Fields{"deviceId": onuDevice.Id, "sn": sn})
+ if err = dh.activateONU(ctx, onuDiscInd.IntfId, int64(onuID), onuDiscInd.SerialNumber, sn); err != nil {
+ return NewErrAdapter("onu-activation-failed", log.Fields{
+ "device-id": onuDevice.Id,
+ "serial-number": sn}, err).Log()
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) onuIndication(onuInd *oop.OnuIndication) error {
+ serialNumber := dh.stringifySerialNumber(onuInd.SerialNumber)
+
+ kwargs := make(map[string]interface{})
+ ponPort := IntfIDToPortNo(onuInd.GetIntfId(), voltha.Port_PON_OLT)
+ var onuDevice *voltha.Device
+ var err error
+ foundInCache := false
+ log.Debugw("ONU indication key create", log.Fields{"onuId": onuInd.OnuId,
+ "intfId": onuInd.GetIntfId()})
+ onuKey := dh.formOnuKey(onuInd.GetIntfId(), onuInd.OnuId)
+
+ errFields := log.Fields{"device-id": dh.device.Id}
+
+ if onuInCache, ok := dh.onus.Load(onuKey); ok {
+
+ //If ONU id is discovered before then use GetDevice to get onuDevice because it is cheaper.
+ foundInCache = true
+ errFields["onu-id"] = onuInCache.(*OnuDevice).deviceID
+ onuDevice, err = dh.coreProxy.GetDevice(nil, dh.device.Id, onuInCache.(*OnuDevice).deviceID)
+ } else {
+ //If ONU not found in adapter cache then we have to use GetChildDevice to get onuDevice
+ if serialNumber != "" {
+ kwargs["serial_number"] = serialNumber
+ errFields["serial-number"] = serialNumber
+ } else {
+ kwargs["onu_id"] = onuInd.OnuId
+ kwargs["parent_port_no"] = ponPort
+ errFields["onu-id"] = onuInd.OnuId
+ errFields["parent-port-no"] = ponPort
+ }
+ onuDevice, err = dh.coreProxy.GetChildDevice(context.TODO(), dh.device.Id, kwargs)
+ }
+
+ if err != nil || onuDevice == nil {
+ return NewErrNotFound("onu-device", errFields, err).Log()
+ }
+
+ if onuDevice.ParentPortNo != ponPort {
+ log.Warnw("ONU-is-on-a-different-intf-id-now", log.Fields{
+ "previousIntfId": onuDevice.ParentPortNo,
+ "currentIntfId": ponPort})
+ }
+
+ if onuDevice.ProxyAddress.OnuId != onuInd.OnuId {
+ log.Warnw("ONU-id-mismatch, can happen if both voltha and the olt rebooted", log.Fields{
+ "expected_onu_id": onuDevice.ProxyAddress.OnuId,
+ "received_onu_id": onuInd.OnuId})
+ }
+ if !foundInCache {
+ onuKey := dh.formOnuKey(onuInd.GetIntfId(), onuInd.GetOnuId())
+
+ dh.onus.Store(onuKey, NewOnuDevice(onuDevice.Id, onuDevice.Type, onuDevice.SerialNumber, onuInd.GetOnuId(), onuInd.GetIntfId(), onuDevice.ProxyAddress.DeviceId))
+
+ }
+ if err := dh.updateOnuStates(onuDevice, onuInd); err != nil {
+ return NewErrCommunication("state-update-failed", errFields, err).Log()
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) updateOnuStates(onuDevice *voltha.Device, onuInd *oop.OnuIndication) error {
+ ctx := context.TODO()
+ log.Debugw("onu-indication-for-state", log.Fields{"onuIndication": onuInd, "DeviceId": onuDevice.Id, "operStatus": onuDevice.OperStatus, "adminStatus": onuDevice.AdminState})
+ if onuInd.AdminState == "down" {
+ // Tests have shown that we sometimes get OperState as NOT down even if AdminState is down, forcing it
+ if onuInd.OperState != "down" {
+ log.Warnw("ONU-admin-state-down", log.Fields{"operState": onuInd.OperState})
+ onuInd.OperState = "down"
+ }
+ }
+
+ switch onuInd.OperState {
+ case "down":
+ log.Debugw("sending-interadapter-onu-indication", log.Fields{"onuIndication": onuInd, "DeviceId": onuDevice.Id, "operStatus": onuDevice.OperStatus, "adminStatus": onuDevice.AdminState})
+ // TODO NEW CORE do not hardcode adapter name. Handler needs Adapter reference
+ err := dh.AdapterProxy.SendInterAdapterMessage(ctx, onuInd, ic.InterAdapterMessageType_ONU_IND_REQUEST,
+ "openolt", onuDevice.Type, onuDevice.Id, onuDevice.ProxyAddress.DeviceId, "")
+ if err != nil {
+ return NewErrCommunication("inter-adapter-send-failed", log.Fields{
+ "onu-indicator": onuInd,
+ "source": "openolt",
+ "device-type": onuDevice.Type,
+ "device-id": onuDevice.Id}, err).Log()
+ }
+ case "up":
+ log.Debugw("sending-interadapter-onu-indication", log.Fields{"onuIndication": onuInd, "DeviceId": onuDevice.Id, "operStatus": onuDevice.OperStatus, "adminStatus": onuDevice.AdminState})
+ // TODO NEW CORE do not hardcode adapter name. Handler needs Adapter reference
+ err := dh.AdapterProxy.SendInterAdapterMessage(ctx, onuInd, ic.InterAdapterMessageType_ONU_IND_REQUEST,
+ "openolt", onuDevice.Type, onuDevice.Id, onuDevice.ProxyAddress.DeviceId, "")
+ if err != nil {
+ return NewErrCommunication("inter-adapter-send-failed", log.Fields{
+ "onu-indicator": onuInd,
+ "source": "openolt",
+ "device-type": onuDevice.Type,
+ "device-id": onuDevice.Id}, err).Log()
+ }
+ default:
+ return NewErrInvalidValue(log.Fields{"oper-state": onuInd.OperState}, nil).Log()
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) stringifySerialNumber(serialNum *oop.SerialNumber) string {
+ if serialNum != nil {
+ return string(serialNum.VendorId) + dh.stringifyVendorSpecific(serialNum.VendorSpecific)
+ }
+ return ""
+}
+func (dh *DeviceHandler) deStringifySerialNumber(serialNum string) (*oop.SerialNumber, error) {
+ decodedStr, err := hex.DecodeString(serialNum[4:])
+ if err != nil {
+ return nil, err
+ }
+ return &oop.SerialNumber{
+ VendorId: []byte(serialNum[:4]),
+ VendorSpecific: []byte(decodedStr),
+ }, nil
+}
+
+func (dh *DeviceHandler) stringifyVendorSpecific(vendorSpecific []byte) string {
+ tmp := fmt.Sprintf("%x", (uint32(vendorSpecific[0])>>4)&0x0f) +
+ fmt.Sprintf("%x", uint32(vendorSpecific[0]&0x0f)) +
+ fmt.Sprintf("%x", (uint32(vendorSpecific[1])>>4)&0x0f) +
+ fmt.Sprintf("%x", (uint32(vendorSpecific[1]))&0x0f) +
+ fmt.Sprintf("%x", (uint32(vendorSpecific[2])>>4)&0x0f) +
+ fmt.Sprintf("%x", (uint32(vendorSpecific[2]))&0x0f) +
+ fmt.Sprintf("%x", (uint32(vendorSpecific[3])>>4)&0x0f) +
+ fmt.Sprintf("%x", (uint32(vendorSpecific[3]))&0x0f)
+ return tmp
+}
+
+//UpdateFlowsBulk upates the bulk flow
+func (dh *DeviceHandler) UpdateFlowsBulk() error {
+ return ErrNotImplemented
+}
+
+//GetChildDevice returns the child device for given parent port and onu id
+func (dh *DeviceHandler) GetChildDevice(parentPort, onuID uint32) (*voltha.Device, error) {
+ log.Debugw("GetChildDevice", log.Fields{"pon port": parentPort, "onuID": onuID})
+ kwargs := make(map[string]interface{})
+ kwargs["onu_id"] = onuID
+ kwargs["parent_port_no"] = parentPort
+ onuDevice, err := dh.coreProxy.GetChildDevice(context.TODO(), dh.device.Id, kwargs)
+ if err != nil {
+ return nil, NewErrNotFound("onu", log.Fields{
+ "interface-id": parentPort,
+ "onu-id": onuID}, err).Log()
+ }
+ log.Debugw("Successfully received child device from core", log.Fields{"child_device": *onuDevice})
+ return onuDevice, nil
+}
+
+// SendPacketInToCore sends packet-in to core
+// For this, it calls SendPacketIn of the core-proxy which uses a device specific topic to send the request.
+// The adapter handling the device creates a device specific topic
+func (dh *DeviceHandler) SendPacketInToCore(logicalPort uint32, packetPayload []byte) error {
+ log.Debugw("send-packet-in-to-core", log.Fields{
+ "port": logicalPort,
+ "packet": hex.EncodeToString(packetPayload),
+ })
+ if err := dh.coreProxy.SendPacketIn(context.TODO(), dh.device.Id, logicalPort, packetPayload); err != nil {
+ return NewErrCommunication("packet-send-failed", log.Fields{
+ "source": "adapter",
+ "destination": "core",
+ "device-id": dh.device.Id,
+ "logical-port": logicalPort,
+ "packet": hex.EncodeToString(packetPayload)}, err).Log()
+ }
+ log.Debugw("Sent packet-in to core successfully", log.Fields{
+ "packet": hex.EncodeToString(packetPayload),
+ })
+ return nil
+}
+
+// AddUniPortToOnu adds the uni port to the onu device
+func (dh *DeviceHandler) AddUniPortToOnu(intfID, onuID, uniPort uint32) {
+ onuKey := dh.formOnuKey(intfID, onuID)
+
+ if onuDevice, ok := dh.onus.Load(onuKey); ok {
+ // add it to the uniPort map for the onu device
+ if _, ok = onuDevice.(*OnuDevice).uniPorts[uniPort]; !ok {
+ onuDevice.(*OnuDevice).uniPorts[uniPort] = struct{}{}
+ log.Debugw("adding-uni-port", log.Fields{"port": uniPort, "intfID": intfID, "onuId": onuID})
+ }
+ }
+}
+
+//UpdateFlowsIncrementally updates the device flow
+func (dh *DeviceHandler) UpdateFlowsIncrementally(ctx context.Context, device *voltha.Device, flows *of.FlowChanges, groups *of.FlowGroupChanges, flowMetadata *voltha.FlowMetadata) error {
+ log.Debugw("Received-incremental-flowupdate-in-device-handler", log.Fields{"deviceID": device.Id, "flows": flows, "groups": groups, "flowMetadata": flowMetadata})
+ if flows != nil {
+ for _, flow := range flows.ToRemove.Items {
+ log.Debug("Removing flow", log.Fields{"deviceId": device.Id, "flowToRemove": flow})
+ dh.flowMgr.RemoveFlow(ctx, flow)
+ }
+
+ for _, flow := range flows.ToAdd.Items {
+ log.Debug("Adding flow", log.Fields{"deviceId": device.Id, "flowToAdd": flow})
+ dh.flowMgr.AddFlow(ctx, flow, flowMetadata)
+ }
+ }
+ if groups != nil && flows != nil {
+ for _, flow := range flows.ToRemove.Items {
+ log.Debug("Removing flow", log.Fields{"deviceID": device.Id, "flowToRemove": flow})
+ // dh.flowMgr.RemoveFlow(flow)
+ }
+ }
+
+ if groups != nil {
+ for _, group := range groups.ToAdd.Items {
+ dh.flowMgr.AddGroup(ctx, group)
+ }
+ for _, group := range groups.ToUpdate.Items {
+ dh.flowMgr.ModifyGroup(ctx, group)
+ }
+ if len(groups.ToRemove.Items) != 0 {
+ log.Debug("Group delete operation is not supported for now")
+ }
+ }
+ log.Debug("UpdateFlowsIncrementally done successfully")
+ return nil
+}
+
+//DisableDevice disables the given device
+//It marks the following for the given device:
+//Device-Handler Admin-State : down
+//Device Port-State: UNKNOWN
+//Device Oper-State: UNKNOWN
+func (dh *DeviceHandler) DisableDevice(device *voltha.Device) error {
+ /* On device disable ,admin state update has to be done prior sending request to agent since
+ the indication thread may processes invalid indications of ONU and OLT*/
+ dh.lockDevice.Lock()
+ dh.adminState = "down"
+ dh.lockDevice.Unlock()
+ if dh.Client != nil {
+ if _, err := dh.Client.DisableOlt(context.Background(), new(oop.Empty)); err != nil {
+ if e, ok := status.FromError(err); ok && e.Code() == codes.Internal {
+ dh.lockDevice.Lock()
+ dh.adminState = "up"
+ dh.lockDevice.Unlock()
+ return NewErrAdapter("olt-disable-failed", log.Fields{"device-id": device.Id}, err).Log()
+ }
+ }
+ }
+ log.Debugw("olt-disabled", log.Fields{"deviceID": device.Id})
+ /* Discovered ONUs entries need to be cleared , since on device disable the child devices goes to
+ UNREACHABLE state which needs to be configured again*/
+
+ dh.discOnus = sync.Map{}
+ dh.onus = sync.Map{}
+
+ go dh.notifyChildDevices("unreachable")
+ cloned := proto.Clone(device).(*voltha.Device)
+ // Update the all pon ports state on that device to disable and NNI remains active as NNI remains active in openolt agent.
+ for _, port := range cloned.Ports {
+ if port.GetType() == voltha.Port_PON_OLT {
+ if err := dh.coreProxy.PortStateUpdate(context.TODO(), cloned.Id,
+ voltha.Port_PON_OLT, port.GetPortNo(), voltha.OperStatus_UNKNOWN); err != nil {
+ return err
+ }
+ }
+ }
+
+ log.Debugw("disable-device-end", log.Fields{"deviceID": device.Id})
+ return nil
+}
+
+func (dh *DeviceHandler) notifyChildDevices(state string) {
+
+ // Update onu state as unreachable in onu adapter
+ onuInd := oop.OnuIndication{}
+ onuInd.OperState = state
+ //get the child device for the parent device
+ onuDevices, err := dh.coreProxy.GetChildDevices(context.TODO(), dh.device.Id)
+ if err != nil {
+ log.Errorw("failed-to-get-child-devices-information", log.Fields{"deviceID": dh.device.Id, "error": err})
+ }
+ if onuDevices != nil {
+ for _, onuDevice := range onuDevices.Items {
+ err := dh.AdapterProxy.SendInterAdapterMessage(context.TODO(), &onuInd, ic.InterAdapterMessageType_ONU_IND_REQUEST,
+ "openolt", onuDevice.Type, onuDevice.Id, onuDevice.ProxyAddress.DeviceId, "")
+ if err != nil {
+ log.Errorw("failed-to-send-inter-adapter-message", log.Fields{"OnuInd": onuInd,
+ "From Adapter": "openolt", "DeviceType": onuDevice.Type, "DeviceID": onuDevice.Id})
+ }
+
+ }
+ }
+
+}
+
+//ReenableDevice re-enables the olt device after disable
+//It marks the following for the given device:
+//Device-Handler Admin-State : up
+//Device Port-State: ACTIVE
+//Device Oper-State: ACTIVE
+func (dh *DeviceHandler) ReenableDevice(device *voltha.Device) error {
+ dh.lockDevice.Lock()
+ dh.adminState = "up"
+ dh.lockDevice.Unlock()
+
+ if _, err := dh.Client.ReenableOlt(context.Background(), new(oop.Empty)); err != nil {
+ if e, ok := status.FromError(err); ok && e.Code() == codes.Internal {
+ dh.lockDevice.Lock()
+ dh.adminState = "down"
+ dh.lockDevice.Unlock()
+ return NewErrAdapter("olt-reenable-failed", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ }
+ log.Debug("olt-reenabled")
+
+ cloned := proto.Clone(device).(*voltha.Device)
+ // Update the all ports state on that device to enable
+
+ if err := dh.disableAdminDownPorts(device); err != nil {
+ return NewErrAdapter("port-status-update-failed-after-olt-reenable", log.Fields{"device": device}, err).Log()
+ }
+ //Update the device oper status as ACTIVE
+ cloned.OperStatus = voltha.OperStatus_ACTIVE
+ dh.device = cloned
+
+ if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), cloned.Id, cloned.ConnectStatus, cloned.OperStatus); err != nil {
+ return NewErrAdapter("state-update-failed", log.Fields{
+ "device-id": device.Id,
+ "connect-status": cloned.ConnectStatus,
+ "oper-status": cloned.OperStatus}, err).Log()
+ }
+
+ log.Debugw("ReEnableDevice-end", log.Fields{"deviceID": device.Id})
+
+ return nil
+}
+
+func (dh *DeviceHandler) clearUNIData(ctx context.Context, onu *rsrcMgr.OnuGemInfo) error {
+ var uniID uint32
+ var err error
+ for _, port := range onu.UniPorts {
+ uniID = UniIDFromPortNum(uint32(port))
+ log.Debugw("clearing-resource-data-for-uni-port", log.Fields{"port": port, "uniID": uniID})
+ /* Delete tech-profile instance from the KV store */
+ if err = dh.flowMgr.DeleteTechProfileInstances(ctx, onu.IntfID, onu.OnuID, uniID, onu.SerialNumber); err != nil {
+ log.Debugw("Failed-to-remove-tech-profile-instance-for-onu", log.Fields{"onu-id": onu.OnuID})
+ }
+ log.Debugw("Deleted-tech-profile-instance-for-onu", log.Fields{"onu-id": onu.OnuID})
+ flowIDs := dh.resourceMgr.GetCurrentFlowIDsForOnu(ctx, onu.IntfID, int32(onu.OnuID), int32(uniID))
+ for _, flowID := range flowIDs {
+ dh.resourceMgr.FreeFlowID(ctx, onu.IntfID, int32(onu.OnuID), int32(uniID), flowID)
+ }
+ tpIDList := dh.resourceMgr.GetTechProfileIDForOnu(ctx, onu.IntfID, onu.OnuID, uniID)
+ for _, tpID := range tpIDList {
+ if err = dh.resourceMgr.RemoveMeterIDForOnu(ctx, "upstream", onu.IntfID, onu.OnuID, uniID, tpID); err != nil {
+ log.Debugw("Failed-to-remove-meter-id-for-onu-upstream", log.Fields{"onu-id": onu.OnuID})
+ }
+ log.Debugw("Removed-meter-id-for-onu-upstream", log.Fields{"onu-id": onu.OnuID})
+ if err = dh.resourceMgr.RemoveMeterIDForOnu(ctx, "downstream", onu.IntfID, onu.OnuID, uniID, tpID); err != nil {
+ log.Debugw("Failed-to-remove-meter-id-for-onu-downstream", log.Fields{"onu-id": onu.OnuID})
+ }
+ log.Debugw("Removed-meter-id-for-onu-downstream", log.Fields{"onu-id": onu.OnuID})
+ }
+ dh.resourceMgr.FreePONResourcesForONU(ctx, onu.IntfID, onu.OnuID, uniID)
+ if err = dh.resourceMgr.RemoveTechProfileIDsForOnu(ctx, onu.IntfID, onu.OnuID, uniID); err != nil {
+ log.Debugw("Failed-to-remove-tech-profile-id-for-onu", log.Fields{"onu-id": onu.OnuID})
+ }
+ log.Debugw("Removed-tech-profile-id-for-onu", log.Fields{"onu-id": onu.OnuID})
+ if err = dh.resourceMgr.DelGemPortPktIn(ctx, onu.IntfID, onu.OnuID, uint32(port)); err != nil {
+ log.Debugw("Failed-to-remove-gemport-pkt-in", log.Fields{"intfid": onu.IntfID, "onuid": onu.OnuID, "uniId": uniID})
+ }
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) clearNNIData(ctx context.Context) error {
+ nniUniID := -1
+ nniOnuID := -1
+
+ if dh.resourceMgr == nil {
+ return fmt.Errorf("no resource manager for deviceID %s", dh.deviceID)
+ }
+ //Free the flow-ids for the NNI port
+ nni, err := dh.resourceMgr.GetNNIFromKVStore(ctx)
+ if err != nil {
+ return NewErrPersistence("get", "nni", 0, nil, err).Log()
+ }
+ log.Debugw("NNI are ", log.Fields{"nni": nni})
+ for _, nniIntfID := range nni {
+ flowIDs := dh.resourceMgr.GetCurrentFlowIDsForOnu(ctx, uint32(nniIntfID), int32(nniOnuID), int32(nniUniID))
+ log.Debugw("Current flow ids for nni", log.Fields{"flow-ids": flowIDs})
+ for _, flowID := range flowIDs {
+ dh.resourceMgr.FreeFlowID(ctx, uint32(nniIntfID), -1, -1, uint32(flowID))
+ }
+ dh.resourceMgr.RemoveResourceMap(ctx, nniIntfID, int32(nniOnuID), int32(nniUniID))
+ }
+ if err = dh.resourceMgr.DelNNiFromKVStore(ctx); err != nil {
+ return NewErrPersistence("clear", "nni", 0, nil, err).Log()
+ }
+ return nil
+}
+
+// DeleteDevice deletes the device instance from openolt handler array. Also clears allocated resource manager resources. Also reboots the OLT hardware!
+func (dh *DeviceHandler) DeleteDevice(ctx context.Context, device *voltha.Device) error {
+ log.Debug("Function entry delete device")
+ dh.lockDevice.Lock()
+ if dh.adminState == "deleted" {
+ dh.lockDevice.Unlock()
+ return nil
+ }
+ dh.adminState = "deleted"
+ dh.lockDevice.Unlock()
+ /* Clear the KV store data associated with the all the UNI ports
+ This clears up flow data and also resource map data for various
+ other pon resources like alloc_id and gemport_id
+ */
+ if dh.resourceMgr != nil {
+ noOfPonPorts := dh.resourceMgr.DevInfo.GetPonPorts()
+ var ponPort uint32
+ for ponPort = 0; ponPort < noOfPonPorts; ponPort++ {
+ var onuGemData []rsrcMgr.OnuGemInfo
+ err := dh.resourceMgr.ResourceMgrs[ponPort].GetOnuGemInfo(ctx, ponPort, &onuGemData)
+ if err != nil {
+ return NewErrNotFound("onu", log.Fields{
+ "device-id": dh.device.Id,
+ "pon-port": ponPort}, err).Log()
+ }
+ for _, onu := range onuGemData {
+ onuID := make([]uint32, 1)
+ log.Debugw("onu data ", log.Fields{"onu": onu})
+ if err = dh.clearUNIData(ctx, &onu); err != nil {
+ log.Errorw("Failed to clear data for onu", log.Fields{"onu-device": onu})
+ }
+ // Clear flowids for gem cache.
+ for _, gem := range onu.GemPorts {
+ dh.resourceMgr.DeleteFlowIDsForGem(ctx, ponPort, gem)
+ }
+ onuID[0] = onu.OnuID
+ dh.resourceMgr.FreeonuID(ctx, ponPort, onuID)
+ }
+ dh.resourceMgr.DeleteIntfIDGempMapPath(ctx, ponPort)
+ onuGemData = nil
+ err = dh.resourceMgr.DelOnuGemInfoForIntf(ctx, ponPort)
+ if err != nil {
+ log.Errorw("Failed to update onugem info", log.Fields{"intfid": ponPort, "onugeminfo": onuGemData})
+ }
+ }
+ /* Clear the flows from KV store associated with NNI port.
+ There are mostly trap rules from NNI port (like LLDP)
+ */
+ if err := dh.clearNNIData(ctx); err != nil {
+ log.Errorw("Failed to clear data for NNI port", log.Fields{"device-id": dh.deviceID})
+ }
+
+ /* Clear the resource pool for each PON port in the background */
+ go dh.resourceMgr.Delete(ctx)
+ }
+
+ /*Delete ONU map for the device*/
+ dh.onus.Range(func(key interface{}, value interface{}) bool {
+ dh.onus.Delete(key)
+ return true
+ })
+
+ log.Debug("Removed-device-from-Resource-manager-KV-store")
+ // Stop the Stats collector
+ dh.stopCollector <- true
+ // stop the heartbeat check routine
+ dh.stopHeartbeatCheck <- true
+ //Reset the state
+ if dh.Client != nil {
+ if _, err := dh.Client.Reboot(ctx, new(oop.Empty)); err != nil {
+ return NewErrAdapter("olt-reboot-failed", log.Fields{"device-id": dh.deviceID}, err).Log()
+ }
+ }
+ cloned := proto.Clone(device).(*voltha.Device)
+ cloned.OperStatus = voltha.OperStatus_UNKNOWN
+ cloned.ConnectStatus = voltha.ConnectStatus_UNREACHABLE
+ if err := dh.coreProxy.DeviceStateUpdate(ctx, cloned.Id, cloned.ConnectStatus, cloned.OperStatus); err != nil {
+ return NewErrAdapter("device-state-update-failed", log.Fields{
+ "device-id": device.Id,
+ "connect-status": cloned.ConnectStatus,
+ "oper-status": cloned.OperStatus}, err).Log()
+ }
+ return nil
+}
+
+//RebootDevice reboots the given device
+func (dh *DeviceHandler) RebootDevice(device *voltha.Device) error {
+ if _, err := dh.Client.Reboot(context.Background(), new(oop.Empty)); err != nil {
+ return NewErrAdapter("olt-reboot-failed", log.Fields{"device-id": dh.deviceID}, err).Log()
+ }
+ log.Debugw("rebooted-device-successfully", log.Fields{"deviceID": device.Id})
+ return nil
+}
+
+func (dh *DeviceHandler) handlePacketIndication(ctx context.Context, packetIn *oop.PacketIndication) error {
+ log.Debugw("Received packet-in", log.Fields{
+ "packet-indication": *packetIn,
+ "packet": hex.EncodeToString(packetIn.Pkt),
+ })
+ logicalPortNum, err := dh.flowMgr.GetLogicalPortFromPacketIn(ctx, packetIn)
+ if err != nil {
+ return NewErrNotFound("logical-port", log.Fields{"packet": hex.EncodeToString(packetIn.Pkt)}, err).Log()
+ }
+ log.Debugw("sending packet-in to core", log.Fields{
+ "logicalPortNum": logicalPortNum,
+ "packet": hex.EncodeToString(packetIn.Pkt),
+ })
+ if err := dh.coreProxy.SendPacketIn(context.TODO(), dh.device.Id, logicalPortNum, packetIn.Pkt); err != nil {
+ return NewErrCommunication("send-packet-in", log.Fields{
+ "destination": "core",
+ "source": dh.deviceType,
+ "packet": hex.EncodeToString(packetIn.Pkt)}, err).Log()
+ }
+ log.Debugw("Success sending packet-in to core!", log.Fields{
+ "packet": hex.EncodeToString(packetIn.Pkt),
+ })
+ return nil
+}
+
+// PacketOut sends packet-out from VOLTHA to OLT on the egress port provided
+func (dh *DeviceHandler) PacketOut(ctx context.Context, egressPortNo int, packet *of.OfpPacketOut) error {
+ log.Debugw("incoming-packet-out", log.Fields{
+ "deviceID": dh.deviceID,
+ "egress_port_no": egressPortNo,
+ "pkt-length": len(packet.Data),
+ "packet": hex.EncodeToString(packet.Data),
+ })
+
+ egressPortType := IntfIDToPortTypeName(uint32(egressPortNo))
+ if egressPortType == voltha.Port_ETHERNET_UNI {
+ outerEthType := (uint16(packet.Data[12]) << 8) | uint16(packet.Data[13])
+ innerEthType := (uint16(packet.Data[16]) << 8) | uint16(packet.Data[17])
+ if outerEthType == 0x8942 || outerEthType == 0x88cc {
+ // Do not packet-out lldp packets on uni port.
+ // ONOS has no clue about uni/nni ports, it just packets out on all
+ // available ports on the Logical Switch. It should not be interested
+ // in the UNI links.
+ log.Debug("dropping-lldp-packet-out-on-uni")
+ return nil
+ }
+ if outerEthType == 0x88a8 || outerEthType == 0x8100 {
+ if innerEthType == 0x8100 {
+ // q-in-q 802.1ad or 802.1q double tagged packet.
+ // slice out the outer tag.
+ packet.Data = append(packet.Data[:12], packet.Data[16:]...)
+ log.Debugw("packet-now-single-tagged", log.Fields{"packetData": hex.EncodeToString(packet.Data)})
+ }
+ }
+ intfID := IntfIDFromUniPortNum(uint32(egressPortNo))
+ onuID := OnuIDFromPortNum(uint32(egressPortNo))
+ uniID := UniIDFromPortNum(uint32(egressPortNo))
+
+ gemPortID, err := dh.flowMgr.GetPacketOutGemPortID(ctx, intfID, onuID, uint32(egressPortNo))
+ if err != nil {
+ // In this case the openolt agent will receive the gemPortID as 0.
+ // The agent tries to retrieve the gemPortID in this case.
+ // This may not always succeed at the agent and packetOut may fail.
+ log.Errorw("failed-to-retrieve-gemport-id-for-packet-out", log.Fields{
+ "packet": hex.EncodeToString(packet.Data),
+ })
+ }
+
+ onuPkt := oop.OnuPacket{IntfId: intfID, OnuId: onuID, PortNo: uint32(egressPortNo), GemportId: gemPortID, Pkt: packet.Data}
+
+ log.Debugw("sending-packet-to-onu", log.Fields{
+ "egress_port_no": egressPortNo,
+ "IntfId": intfID,
+ "onuID": onuID,
+ "uniID": uniID,
+ "gemPortID": gemPortID,
+ "packet": hex.EncodeToString(packet.Data),
+ })
+
+ if _, err := dh.Client.OnuPacketOut(ctx, &onuPkt); err != nil {
+ return NewErrCommunication("packet-out-send", log.Fields{
+ "source": "adapter",
+ "destination": "onu",
+ "egress-port-number": egressPortNo,
+ "interface-id": intfID,
+ "oni-id": onuID,
+ "uni-id": uniID,
+ "gem-port-id": gemPortID,
+ "packet": hex.EncodeToString(packet.Data)}, err).Log()
+ }
+ } else if egressPortType == voltha.Port_ETHERNET_NNI {
+ nniIntfID, err := IntfIDFromNniPortNum(uint32(egressPortNo))
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"egress-nni-port": egressPortNo}, err).Log()
+ }
+ uplinkPkt := oop.UplinkPacket{IntfId: nniIntfID, Pkt: packet.Data}
+
+ log.Debugw("sending-packet-to-nni", log.Fields{
+ "uplink_pkt": uplinkPkt,
+ "packet": hex.EncodeToString(packet.Data),
+ })
+
+ if _, err := dh.Client.UplinkPacketOut(ctx, &uplinkPkt); err != nil {
+ return NewErrCommunication("packet-out-to-nni", log.Fields{"packet": hex.EncodeToString(packet.Data)}, err).Log()
+ }
+ } else {
+ log.Warnw("Packet-out-to-this-interface-type-not-implemented", log.Fields{
+ "egress_port_no": egressPortNo,
+ "egressPortType": egressPortType,
+ "packet": hex.EncodeToString(packet.Data),
+ })
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) formOnuKey(intfID, onuID uint32) string {
+ return "" + strconv.Itoa(int(intfID)) + "." + strconv.Itoa(int(onuID))
+}
+
+func startHeartbeatCheck(dh *DeviceHandler) {
+ // start the heartbeat check towards the OLT.
+ var timerCheck *time.Timer
+
+ for {
+ heartbeatTimer := time.NewTimer(dh.openOLT.HeartbeatCheckInterval)
+ select {
+ case <-heartbeatTimer.C:
+ ctx, cancel := context.WithTimeout(context.Background(), dh.openOLT.GrpcTimeoutInterval)
+ if heartBeat, err := dh.Client.HeartbeatCheck(ctx, new(oop.Empty)); err != nil {
+ log.Error("Hearbeat failed")
+ if timerCheck == nil {
+ // start a after func, when expired will update the state to the core
+ timerCheck = time.AfterFunc(dh.openOLT.HeartbeatFailReportInterval, dh.updateStateUnreachable)
+ }
+ } else {
+ if timerCheck != nil {
+ if timerCheck.Stop() {
+ log.Debug("We got hearbeat within the timeout")
+ } else {
+
+ log.Debug("We got hearbeat after the timeout expired, changing the states")
+ go dh.notifyChildDevices("up")
+ if err := dh.coreProxy.DeviceStateUpdate(ctx, dh.device.Id, voltha.ConnectStatus_REACHABLE,
+ voltha.OperStatus_ACTIVE); err != nil {
+ log.Errorw("Failed to update device state", log.Fields{"deviceID": dh.device.Id, "error": err})
+ }
+ }
+ timerCheck = nil
+ }
+ log.Debugw("Hearbeat", log.Fields{"signature": heartBeat})
+ }
+ cancel()
+ case <-dh.stopHeartbeatCheck:
+ log.Debug("Stopping heart beat check")
+ return
+ }
+ }
+}
+
+func (dh *DeviceHandler) updateStateUnreachable() {
+
+ go dh.notifyChildDevices("unreachable")
+ if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.device.Id, voltha.ConnectStatus_UNREACHABLE, voltha.OperStatus_UNKNOWN); err != nil {
+ NewErrAdapter("device-state-update-failed", log.Fields{"device-id": dh.device.Id}, err).LogAt(log.ErrorLevel)
+ }
+}
+
+// EnablePort to enable Pon interface
+func (dh *DeviceHandler) EnablePort(port *voltha.Port) error {
+ log.Debugw("enable-port", log.Fields{"Device": dh.device, "port": port})
+ return dh.modifyPhyPort(port, true)
+}
+
+// DisablePort to disable pon interface
+func (dh *DeviceHandler) DisablePort(port *voltha.Port) error {
+ log.Debugw("disable-port", log.Fields{"Device": dh.device, "port": port})
+ return dh.modifyPhyPort(port, false)
+}
+
+//modifyPhyPort is common function to enable and disable the port. parm :enablePort, true to enablePort and false to disablePort.
+func (dh *DeviceHandler) modifyPhyPort(port *voltha.Port, enablePort bool) error {
+ ctx := context.Background()
+ log.Infow("modifyPhyPort", log.Fields{"port": port, "Enable": enablePort})
+ if port.GetType() == voltha.Port_ETHERNET_NNI {
+ // Bug is opened for VOL-2505 to support NNI disable feature.
+ log.Infow("voltha-supports-single-nni-hence-disable-of-nni-not-allowed",
+ log.Fields{"Device": dh.device, "port": port})
+ return NewErrAdapter("illegal-port-request", log.Fields{
+ "port-type": port.GetType,
+ "enable-state": enablePort}, nil).Log()
+ }
+ // fetch interfaceid from PortNo
+ ponID := PortNoToIntfID(port.GetPortNo(), voltha.Port_PON_OLT)
+ ponIntf := &oop.Interface{IntfId: ponID}
+ var operStatus voltha.OperStatus_Types
+ if enablePort {
+ operStatus = voltha.OperStatus_ACTIVE
+ out, err := dh.Client.EnablePonIf(ctx, ponIntf)
+
+ if err != nil {
+ return NewErrAdapter("pon-port-enable-failed", log.Fields{
+ "device-id": dh.device.Id,
+ "port": port}, err).Log()
+ }
+ // updating interface local cache for collecting stats
+ dh.activePorts.Store(ponID, true)
+ log.Infow("enabled-pon-port", log.Fields{"out": out, "DeviceID": dh.device, "Port": port})
+ } else {
+ operStatus = voltha.OperStatus_UNKNOWN
+ out, err := dh.Client.DisablePonIf(ctx, ponIntf)
+ if err != nil {
+ return NewErrAdapter("pon-port-disable-failed", log.Fields{
+ "device-id": dh.device.Id,
+ "port": port}, err).Log()
+ }
+ // updating interface local cache for collecting stats
+ dh.activePorts.Store(ponID, false)
+ log.Infow("disabled-pon-port", log.Fields{"out": out, "DeviceID": dh.device, "Port": port})
+ }
+ if err := dh.coreProxy.PortStateUpdate(ctx, dh.deviceID, voltha.Port_PON_OLT, port.PortNo, operStatus); err != nil {
+ return NewErrAdapter("port-state-update-failed", log.Fields{
+ "device-id": dh.deviceID,
+ "port": port.PortNo}, err).Log()
+ }
+ return nil
+}
+
+//disableAdminDownPorts disables the ports, if the corresponding port Adminstate is disabled on reboot and Renable device.
+func (dh *DeviceHandler) disableAdminDownPorts(device *voltha.Device) error {
+ cloned := proto.Clone(device).(*voltha.Device)
+ // Disable the port and update the oper_port_status to core
+ // if the Admin state of the port is disabled on reboot and re-enable device.
+ for _, port := range cloned.Ports {
+ if port.AdminState == common.AdminState_DISABLED {
+ if err := dh.DisablePort(port); err != nil {
+ return NewErrAdapter("port-disable-failed", log.Fields{
+ "device-id": dh.deviceID,
+ "port": port}, err).Log()
+ }
+ }
+ }
+ return nil
+}
+
+//populateActivePorts to populate activePorts map
+func (dh *DeviceHandler) populateActivePorts(device *voltha.Device) {
+ log.Info("populateActiveports", log.Fields{"Device": device})
+ for _, port := range device.Ports {
+ if port.Type == voltha.Port_ETHERNET_NNI {
+ if port.OperStatus == voltha.OperStatus_ACTIVE {
+ dh.activePorts.Store(PortNoToIntfID(port.PortNo, voltha.Port_ETHERNET_NNI), true)
+ } else {
+ dh.activePorts.Store(PortNoToIntfID(port.PortNo, voltha.Port_ETHERNET_NNI), false)
+ }
+ }
+ if port.Type == voltha.Port_PON_OLT {
+ if port.OperStatus == voltha.OperStatus_ACTIVE {
+ dh.activePorts.Store(PortNoToIntfID(port.PortNo, voltha.Port_PON_OLT), true)
+ } else {
+ dh.activePorts.Store(PortNoToIntfID(port.PortNo, voltha.Port_PON_OLT), false)
+ }
+ }
+ }
+}
+
+// ChildDeviceLost deletes ONU and clears pon resources related to it.
+func (dh *DeviceHandler) ChildDeviceLost(ctx context.Context, pPortNo uint32, onuID uint32) error {
+ log.Debugw("child-device-lost", log.Fields{"pdeviceID": dh.device.Id})
+ IntfID := PortNoToIntfID(pPortNo, voltha.Port_PON_OLT)
+ onuKey := dh.formOnuKey(IntfID, onuID)
+ onuDevice, ok := dh.onus.Load(onuKey)
+ if !ok {
+ return NewErrAdapter("failed-to-load-onu-details",
+ log.Fields{
+ "device-id": dh.deviceID,
+ "onu-id": onuID,
+ "interface-id": IntfID}, nil).Log()
+ }
+ var sn *oop.SerialNumber
+ var err error
+ if sn, err = dh.deStringifySerialNumber(onuDevice.(*OnuDevice).serialNumber); err != nil {
+ return NewErrAdapter("failed-to-destringify-serial-number",
+ log.Fields{
+ "devicer-id": dh.deviceID,
+ "serial-number": onuDevice.(*OnuDevice).serialNumber}, err).Log()
+ }
+ onu := &oop.Onu{IntfId: IntfID, OnuId: onuID, SerialNumber: sn}
+ if _, err := dh.Client.DeleteOnu(context.Background(), onu); err != nil {
+ return NewErrAdapter("failed-to-delete-onu", log.Fields{
+ "device-id": dh.deviceID,
+ "onu-id": onuID}, err).Log()
+ }
+ //clear PON resources associated with ONU
+ var onuGemData []rsrcMgr.OnuGemInfo
+ if err := dh.resourceMgr.ResourceMgrs[IntfID].GetOnuGemInfo(ctx, IntfID, &onuGemData); err != nil {
+ log.Errorw("Failed-to-get-onu-info-for-pon-port ", log.Fields{
+ "device-id": dh.deviceID,
+ "interface-id": IntfID,
+ "error": err})
+ }
+
+ for i, onu := range onuGemData {
+ if onu.OnuID == onuID && onu.SerialNumber == onuDevice.(*OnuDevice).serialNumber {
+ log.Debugw("onu-data ", log.Fields{"onu": onu})
+ if err := dh.clearUNIData(ctx, &onu); err != nil {
+ log.Errorw("Failed-to-clear-uni-data-for-onu", log.Fields{
+ "device-id": dh.deviceID,
+ "onu-device": onu,
+ "error": err})
+ }
+ // Clear flowids for gem cache.
+ for _, gem := range onu.GemPorts {
+ dh.resourceMgr.DeleteFlowIDsForGem(ctx, IntfID, gem)
+ }
+ onuGemData = append(onuGemData[:i], onuGemData[i+1:]...)
+ dh.resourceMgr.UpdateOnuGemInfo(ctx, IntfID, onuGemData)
+
+ dh.resourceMgr.FreeonuID(ctx, IntfID, []uint32{onu.OnuID})
+ break
+ }
+ }
+ dh.onus.Delete(onuKey)
+ dh.discOnus.Delete(onuDevice.(*OnuDevice).serialNumber)
+ return nil
+}
diff --git a/internal/pkg/core/device_handler_test.go b/internal/pkg/core/device_handler_test.go
new file mode 100644
index 0000000..1d1192e
--- /dev/null
+++ b/internal/pkg/core/device_handler_test.go
@@ -0,0 +1,1171 @@
+/*
+ * 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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "context"
+ "net"
+ "reflect"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/pmmetrics"
+
+ "github.com/golang/protobuf/ptypes"
+ "github.com/golang/protobuf/ptypes/any"
+ "github.com/opencord/voltha-lib-go/v3/pkg/db"
+ fu "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ ponrmgr "github.com/opencord/voltha-lib-go/v3/pkg/ponresourcemanager"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/resourcemanager"
+ "github.com/opencord/voltha-openolt-adapter/pkg/mocks"
+ ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+ 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"
+)
+
+func init() {
+ _, _ = log.AddPackage(log.JSON, log.DebugLevel, nil)
+}
+
+func newMockCoreProxy() *mocks.MockCoreProxy {
+ mcp := mocks.MockCoreProxy{}
+ mcp.Devices = make(map[string]*voltha.Device)
+ var pm []*voltha.PmConfig
+ mcp.Devices["olt"] = &voltha.Device{
+
+ Id: "olt",
+ Root: true,
+ ParentId: "logical_device",
+ ParentPortNo: 1,
+
+ Ports: []*voltha.Port{
+ {PortNo: 1, Label: "pon"},
+ {PortNo: 2, Label: "nni"},
+ },
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ DeviceId: "olt",
+ DeviceType: "onu",
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ },
+ ConnectStatus: 1,
+ PmConfigs: &voltha.PmConfigs{
+ DefaultFreq: 10,
+ Id: "olt",
+ FreqOverride: false,
+ Grouped: false,
+ Metrics: pm,
+ },
+ }
+ mcp.Devices["onu1"] = &voltha.Device{
+
+ Id: "1",
+ Root: false,
+ ParentId: "olt",
+ ParentPortNo: 1,
+
+ Ports: []*voltha.Port{
+ {PortNo: 1, Label: "pon"},
+ {PortNo: 2, Label: "uni"},
+ },
+ OperStatus: 4,
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ OnuId: 1,
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ },
+ ConnectStatus: 1,
+ PmConfigs: &voltha.PmConfigs{
+ DefaultFreq: 10,
+ Id: "olt",
+ FreqOverride: false,
+ Grouped: false,
+ Metrics: pm,
+ },
+ }
+ mcp.Devices["onu2"] = &voltha.Device{
+ Id: "2",
+ Root: false,
+ ParentId: "olt",
+ OperStatus: 2,
+ Ports: []*voltha.Port{
+ {PortNo: 1, Label: "pon"},
+ {PortNo: 2, Label: "uni"},
+ },
+
+ ParentPortNo: 1,
+
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ OnuId: 2,
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ },
+ ConnectStatus: 1,
+ PmConfigs: &voltha.PmConfigs{
+ DefaultFreq: 10,
+ Id: "olt",
+ FreqOverride: false,
+ Grouped: false,
+ Metrics: pm,
+ },
+ }
+ return &mcp
+}
+func newMockDeviceHandler() *DeviceHandler {
+ device := &voltha.Device{
+ Id: "olt",
+ Root: true,
+ ParentId: "logical_device",
+ Ports: []*voltha.Port{
+ {PortNo: 1, Label: "pon", Type: voltha.Port_PON_OLT},
+ {PortNo: 2, Label: "nni", Type: voltha.Port_ETHERNET_NNI},
+ },
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ DeviceId: "olt",
+ DeviceType: "onu",
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ },
+ ConnectStatus: 1,
+ }
+ cp := newMockCoreProxy()
+ ap := &mocks.MockAdapterProxy{}
+ ep := &mocks.MockEventProxy{}
+ openOLT := &OpenOLT{coreProxy: cp, adapterProxy: ap, eventProxy: ep}
+ dh := NewDeviceHandler(cp, ap, ep, device, openOLT)
+ deviceInf := &oop.DeviceInfo{Vendor: "openolt", Ranges: nil, Model: "openolt", DeviceId: dh.deviceID}
+ dh.resourceMgr = &resourcemanager.OpenOltResourceMgr{DeviceID: dh.deviceID, DeviceType: dh.deviceType, DevInfo: deviceInf,
+ KVStore: &db.Backend{
+ Client: &mocks.MockKVClient{},
+ }}
+ dh.resourceMgr.ResourceMgrs = make(map[uint32]*ponrmgr.PONResourceManager)
+ ranges := make(map[string]interface{})
+ sharedIdxByType := make(map[string]string)
+ sharedIdxByType["ALLOC_ID"] = "ALLOC_ID"
+ sharedIdxByType["ONU_ID"] = "ONU_ID"
+ sharedIdxByType["GEMPORT_ID"] = "GEMPORT_ID"
+ sharedIdxByType["FLOW_ID"] = "FLOW_ID"
+ ranges["ONU_ID"] = uint32(0)
+ ranges["GEMPORT_ID"] = uint32(0)
+ ranges["ALLOC_ID"] = uint32(0)
+ ranges["FLOW_ID"] = uint32(0)
+ ranges["onu_id_shared"] = uint32(0)
+ ranges["alloc_id_shared"] = uint32(0)
+ ranges["gemport_id_shared"] = uint32(0)
+ ranges["flow_id_shared"] = uint32(0)
+
+ ponmgr := &ponrmgr.PONResourceManager{
+ DeviceID: "onu-1",
+ IntfIDs: []uint32{1, 2},
+ KVStore: &db.Backend{
+ Client: &mocks.MockKVClient{},
+ },
+ PonResourceRanges: ranges,
+ SharedIdxByType: sharedIdxByType,
+ }
+ dh.resourceMgr.ResourceMgrs[1] = ponmgr
+ dh.resourceMgr.ResourceMgrs[2] = ponmgr
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ dh.flowMgr = NewFlowManager(ctx, dh, dh.resourceMgr)
+ dh.Client = &mocks.MockOpenoltClient{}
+ dh.eventMgr = &OpenOltEventMgr{eventProxy: &mocks.MockEventProxy{}, handler: dh}
+ dh.transitionMap = &TransitionMap{}
+ dh.portStats = &OpenOltStatisticsMgr{}
+
+ var pmNames = []string{
+ "rx_bytes",
+ "rx_packets",
+ "rx_mcast_packets",
+ "rx_bcast_packets",
+ "tx_bytes",
+ "tx_packets",
+ "tx_mcast_packets",
+ "tx_bcast_packets",
+ }
+
+ dh.metrics = pmmetrics.NewPmMetrics(device.Id, pmmetrics.Frequency(2), pmmetrics.FrequencyOverride(false), pmmetrics.Grouped(false), pmmetrics.Metrics(pmNames))
+ return dh
+}
+
+func negativeDeviceHandler() *DeviceHandler {
+ dh := newMockDeviceHandler()
+ device := dh.device
+ device.Id = ""
+ dh.adminState = "down"
+ return dh
+}
+func Test_generateMacFromHost(t *testing.T) {
+ type args struct {
+ host string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ wantErr bool
+ }{
+ {"generateMacFromHost-1", args{host: "localhost"}, "00:00:7f:00:00:01", false},
+ {"generateMacFromHost-2", args{host: "10.10.10.10"}, "00:00:0a:0a:0a:0a", false},
+ //{"generateMacFromHost-3", args{host: "google.com"}, "00:00:d8:3a:c8:8e", false},
+ {"generateMacFromHost-4", args{host: "testing3"}, "", true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := generateMacFromHost(tt.args.host)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("generateMacFromHost() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("generateMacFromHost() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+func Test_macifyIP(t *testing.T) {
+ type args struct {
+ ip net.IP
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{{
+ "macifyIP-1",
+ args{ip: net.ParseIP("10.10.10.10")},
+ "00:00:0a:0a:0a:0a",
+ },
+ {
+ "macifyIP-2",
+ args{ip: net.ParseIP("127.0.0.1")},
+ "00:00:7f:00:00:01",
+ },
+ {
+ "macifyIP-3",
+ args{ip: net.ParseIP("127.0.0.1/24")},
+ "",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := macifyIP(tt.args.ip); got != tt.want {
+ t.Errorf("macifyIP() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func sparseCompare(keys []string, spec, target interface{}) bool {
+ if spec == target {
+ return true
+ }
+ if spec == nil || target == nil {
+ return false
+ }
+ typeSpec := reflect.TypeOf(spec)
+ typeTarget := reflect.TypeOf(target)
+ if typeSpec != typeTarget {
+ return false
+ }
+
+ vSpec := reflect.ValueOf(spec)
+ vTarget := reflect.ValueOf(target)
+ if vSpec.Kind() == reflect.Ptr {
+ vSpec = vSpec.Elem()
+ vTarget = vTarget.Elem()
+ }
+
+ for _, key := range keys {
+ fSpec := vSpec.FieldByName(key)
+ fTarget := vTarget.FieldByName(key)
+ if !reflect.DeepEqual(fSpec.Interface(), fTarget.Interface()) {
+ return false
+ }
+ }
+ return true
+}
+
+func TestDeviceHandler_GetChildDevice(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ type args struct {
+ parentPort uint32
+ onuID uint32
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ want *voltha.Device
+ errType reflect.Type
+ }{
+ {"GetChildDevice-1", dh1,
+ args{parentPort: 1,
+ onuID: 1},
+ &voltha.Device{
+ Id: "1",
+ ParentId: "olt",
+ ParentPortNo: 1,
+ },
+ nil,
+ },
+ {"GetChildDevice-2", dh2,
+ args{parentPort: 1,
+ onuID: 1},
+ nil,
+ reflect.TypeOf(&ErrNotFound{}),
+ },
+ }
+
+ /*
+ --- FAIL: TestDeviceHandler_GetChildDevice/GetChildDevice-1 (0.00s)
+ device_handler_test.go:309: GetportLabel() => want=(, <nil>) got=(id:"1" parent_id:"olt" parent_port_no:1 proxy_address:<channel_id:1 channel_group_id:1 onu_id:1 > oper_status:ACTIVE connect_status:UNREACHABLE ports:<port_no:1 label:"pon" > ports:<port_no:2 label:"uni" > pm_configs:<id:"olt" default_freq:10 > , <nil>)
+ --- FAIL: TestDeviceHandler_GetChildDevice/GetChildDevice-2 (0.00s)
+ */
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := tt.devicehandler.GetChildDevice(tt.args.parentPort, tt.args.onuID)
+ if reflect.TypeOf(err) != tt.errType || !sparseCompare([]string{"Id", "ParentId", "ParentPortNo"}, tt.want, got) {
+ t.Errorf("GetportLabel() => want=(%v, %v) got=(%v, %v)",
+ tt.want, tt.errType, got, reflect.TypeOf(err))
+ return
+ }
+ t.Log("onu device id", got)
+ })
+ }
+}
+
+func TestGetportLabel(t *testing.T) {
+ invalid := reflect.TypeOf(&ErrInvalidValue{})
+ type args struct {
+ portNum uint32
+ portType voltha.Port_PortType
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ errType reflect.Type
+ }{
+ {"GetportLabel-1", args{portNum: 0, portType: 0}, "", invalid},
+ {"GetportLabel-2", args{portNum: 1, portType: 1}, "nni-1", nil},
+ {"GetportLabel-3", args{portNum: 2, portType: 2}, "", invalid},
+ {"GetportLabel-4", args{portNum: 3, portType: 3}, "pon-3", nil},
+ {"GetportLabel-5", args{portNum: 4, portType: 4}, "", invalid},
+ {"GetportLabel-6", args{portNum: 5, portType: 5}, "", invalid},
+ {"GetportLabel-7", args{portNum: 6, portType: 6}, "", invalid},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := GetportLabel(tt.args.portNum, tt.args.portType)
+ if reflect.TypeOf(err) != tt.errType || got != tt.want {
+ t.Errorf("GetportLabel() => want=(%v, %v) got=(%v, %v)",
+ tt.want, tt.errType, got, reflect.TypeOf(err))
+ }
+
+ })
+ }
+}
+
+func TestDeviceHandler_ProcessInterAdapterMessage(t *testing.T) {
+ dh := newMockDeviceHandler()
+ proxyAddr := dh.device.ProxyAddress
+ body := &ic.InterAdapterOmciMessage{
+ Message: []byte("asdfasdfasdfasdfas"),
+ ProxyAddress: proxyAddr,
+ }
+ body2 := &ic.InterAdapterOmciMessage{
+ Message: []byte("asdfasdfasdfasdfas"),
+ //ProxyAddress: &voltha.Device_ProxyAddress{},
+ }
+ body3 := &ic.InterAdapterTechProfileDownloadMessage{}
+ var marshalledData *any.Any
+ var err error
+
+ if marshalledData, err = ptypes.MarshalAny(body); err != nil {
+ log.Errorw("cannot-marshal-request", log.Fields{"error": err})
+ }
+
+ var marshalledData1 *any.Any
+
+ if marshalledData1, err = ptypes.MarshalAny(body2); err != nil {
+ log.Errorw("cannot-marshal-request", log.Fields{"error": err})
+ }
+ var marshalledData2 *any.Any
+
+ if marshalledData2, err = ptypes.MarshalAny(body3); err != nil {
+ log.Errorw("cannot-marshal-request", log.Fields{"error": err})
+ }
+ type args struct {
+ msg *ic.InterAdapterMessage
+ }
+ invalid := reflect.TypeOf(&ErrInvalidValue{})
+ tests := []struct {
+ name string
+ args args
+ wantErr reflect.Type
+ }{
+ {"ProcessInterAdapterMessage-1", args{msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "012345",
+ Type: ic.InterAdapterMessageType_FLOW_REQUEST,
+ },
+ Body: marshalledData,
+ }}, invalid},
+ {"ProcessInterAdapterMessage-2", args{msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "012345",
+ Type: ic.InterAdapterMessageType_FLOW_RESPONSE,
+ },
+ Body: marshalledData1,
+ }}, invalid},
+ {"ProcessInterAdapterMessage-3", args{msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "012345",
+ Type: ic.InterAdapterMessageType_OMCI_REQUEST,
+ },
+ Body: marshalledData,
+ }}, reflect.TypeOf(&ErrCommunication{})},
+ {"ProcessInterAdapterMessage-4", args{msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "012345",
+ Type: ic.InterAdapterMessageType_OMCI_RESPONSE,
+ }, Body: marshalledData,
+ }}, invalid},
+ {"ProcessInterAdapterMessage-5", args{msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "012345",
+ Type: ic.InterAdapterMessageType_METRICS_REQUEST,
+ }, Body: marshalledData1,
+ }}, invalid},
+ {"ProcessInterAdapterMessage-6", args{msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "012345",
+ Type: ic.InterAdapterMessageType_METRICS_RESPONSE,
+ }, Body: marshalledData,
+ }}, invalid},
+ {"ProcessInterAdapterMessage-7", args{msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "012345",
+ Type: ic.InterAdapterMessageType_ONU_IND_REQUEST,
+ }, Body: marshalledData,
+ }}, invalid},
+ {"ProcessInterAdapterMessage-8", args{msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "012345",
+ Type: ic.InterAdapterMessageType_ONU_IND_RESPONSE,
+ }, Body: marshalledData,
+ }}, invalid},
+ {"ProcessInterAdapterMessage-9", args{msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "012345",
+ Type: ic.InterAdapterMessageType_TECH_PROFILE_DOWNLOAD_REQUEST,
+ }, Body: marshalledData,
+ }}, invalid},
+ {"ProcessInterAdapterMessage-10", args{msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "012345",
+ Type: ic.InterAdapterMessageType_DELETE_GEM_PORT_REQUEST,
+ }, Body: marshalledData2,
+ }}, invalid},
+ {"ProcessInterAdapterMessage-11", args{msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "012345",
+ Type: ic.InterAdapterMessageType_DELETE_TCONT_REQUEST,
+ }, Body: marshalledData2,
+ }}, invalid},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+
+ if err := dh.ProcessInterAdapterMessage(tt.args.msg); reflect.TypeOf(err) != tt.wantErr {
+ t.Errorf("DeviceHandler.ProcessInterAdapterMessage() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestDeviceHandler_sendProxiedMessage(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ device1 := &voltha.Device{
+ Id: "onu1",
+ Root: false,
+ ParentId: "logical_device",
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ DeviceId: "onu1",
+ DeviceType: "onu",
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ },
+ ConnectStatus: 1,
+ }
+ device2 := device1
+ device2.ConnectStatus = 2
+ iaomciMsg1 := &ic.InterAdapterOmciMessage{
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ DeviceId: "onu2",
+ DeviceType: "onu",
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ //OnuId: 2,
+ },
+ ConnectStatus: 1,
+ }
+ iaomciMsg2 := &ic.InterAdapterOmciMessage{
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ DeviceId: "onu3",
+ DeviceType: "onu",
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ },
+ ConnectStatus: 1,
+ }
+ type args struct {
+ onuDevice *voltha.Device
+ omciMsg *ic.InterAdapterOmciMessage
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ }{
+ {"sendProxiedMessage-1", dh1, args{onuDevice: device1, omciMsg: &ic.InterAdapterOmciMessage{}}},
+ {"sendProxiedMessage-2", dh1, args{onuDevice: device2, omciMsg: &ic.InterAdapterOmciMessage{}}},
+ {"sendProxiedMessage-3", dh1, args{onuDevice: nil, omciMsg: iaomciMsg1}},
+ {"sendProxiedMessage-4", dh1, args{onuDevice: nil, omciMsg: iaomciMsg2}},
+ {"sendProxiedMessage-5", dh2, args{onuDevice: nil, omciMsg: iaomciMsg2}},
+ {"sendProxiedMessage-6", dh2, args{onuDevice: device1, omciMsg: &ic.InterAdapterOmciMessage{}}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tt.devicehandler.sendProxiedMessage(tt.args.onuDevice, tt.args.omciMsg)
+ })
+ }
+}
+
+func TestDeviceHandler_SendPacketInToCore(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+
+ type args struct {
+ logicalPort uint32
+ packetPayload []byte
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ }{
+ {"SendPacketInToCore-1", dh1, args{logicalPort: 1, packetPayload: []byte("test1")}},
+ {"SendPacketInToCore-2", dh1, args{logicalPort: 1, packetPayload: []byte("")}},
+ {"SendPacketInToCore-3", dh2, args{logicalPort: 1, packetPayload: []byte("test1")}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tt.devicehandler.SendPacketInToCore(tt.args.logicalPort, tt.args.packetPayload)
+ })
+ }
+}
+
+func TestDeviceHandler_DisableDevice(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ wantErr bool
+ }{
+ {"DisableDevice-1", dh1, args{device: dh1.device}, false},
+ {"DisableDevice-2", dh1, args{device: dh2.device}, true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+
+ if err := tt.devicehandler.DisableDevice(tt.args.device); (err != nil) != tt.wantErr {
+ t.Errorf("DeviceHandler.DisableDevice() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestDeviceHandler_ReenableDevice(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ wantErr bool
+ }{
+ {"ReenableDevice-1", dh1, args{device: dh1.device}, false},
+ {"ReenableDevice-2", dh1, args{device: &voltha.Device{}}, true},
+ {"ReenableDevice-3", dh2, args{device: dh1.device}, false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ dh := tt.devicehandler
+ if err := dh.ReenableDevice(tt.args.device); (err != nil) != tt.wantErr {
+ t.Errorf("DeviceHandler.ReenableDevice() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestDeviceHandler_RebootDevice(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := newMockDeviceHandler()
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"RebootDevice-1", dh1, args{device: dh1.device}, false},
+ {"RebootDevice-2", dh1, args{device: dh2.device}, true},
+ {"RebootDevice-3", dh2, args{device: dh2.device}, false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+
+ if err := tt.devicehandler.RebootDevice(tt.args.device); (err != nil) != tt.wantErr {
+ t.Errorf("DeviceHandler.RebootDevice() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestDeviceHandler_handleIndication(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ dh3 := newMockDeviceHandler()
+ dh3.onus = sync.Map{}
+ dh3.onus.Store("onu1", NewOnuDevice("onu1", "onu1", "onu1", 1, 1, "onu1"))
+ dh3.onus.Store("onu2", NewOnuDevice("onu2", "onu2", "onu2", 2, 2, "onu2"))
+
+ type args struct {
+ indication *oop.Indication
+ }
+ tests := []struct {
+ name string
+ deviceHandler *DeviceHandler
+ args args
+ }{
+ // TODO: Add test cases.
+ {"handleIndication-1", dh1, args{indication: &oop.Indication{Data: &oop.Indication_OltInd{OltInd: &oop.OltIndication{OperState: "up"}}}}},
+ {"handleIndication-2", dh1, args{indication: &oop.Indication{Data: &oop.Indication_OltInd{OltInd: &oop.OltIndication{OperState: "down"}}}}},
+ {"handleIndication-3", dh1, args{indication: &oop.Indication{Data: &oop.Indication_IntfInd{IntfInd: &oop.IntfIndication{IntfId: 1, OperState: "up"}}}}},
+ {"handleIndication-4", dh1, args{indication: &oop.Indication{Data: &oop.Indication_IntfInd{IntfInd: &oop.IntfIndication{IntfId: 1, OperState: "down"}}}}},
+ {"handleIndication-5", dh1, args{indication: &oop.Indication{Data: &oop.Indication_IntfOperInd{IntfOperInd: &oop.IntfOperIndication{Type: "nni", IntfId: 1, OperState: "up"}}}}},
+ {"handleIndication-6", dh1, args{indication: &oop.Indication{Data: &oop.Indication_IntfOperInd{IntfOperInd: &oop.IntfOperIndication{Type: "pon", IntfId: 1, OperState: "up"}}}}},
+ {"handleIndication-7", dh1, args{indication: &oop.Indication{Data: &oop.Indication_OnuDiscInd{OnuDiscInd: &oop.OnuDiscIndication{IntfId: 1, SerialNumber: &oop.SerialNumber{VendorId: []byte("TWSH"), VendorSpecific: []byte("1234")}}}}}},
+ {"handleIndication-8", dh1, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "up", AdminState: "up"}}}}},
+ {"handleIndication-9", dh1, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "up", AdminState: "down"}}}}},
+ {"handleIndication-10", dh1, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "down", AdminState: "up"}}}}},
+ {"handleIndication-11", dh1, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "down", AdminState: "down"}}}}},
+ {"handleIndication-12", dh1, args{indication: &oop.Indication{Data: &oop.Indication_OmciInd{OmciInd: &oop.OmciIndication{IntfId: 1, OnuId: 1, Pkt: []byte("onu123-random value")}}}}},
+ {"handleIndication-13", dh1, args{indication: &oop.Indication{Data: &oop.Indication_PktInd{PktInd: &oop.PacketIndication{IntfType: "nni", IntfId: 1, GemportId: 1, FlowId: 1234, PortNo: 1}}}}},
+ {"handleIndication-14", dh1, args{indication: &oop.Indication{Data: &oop.Indication_PortStats{PortStats: &oop.PortStatistics{IntfId: 1, RxBytes: 100, RxPackets: 100, RxUcastPackets: 100, RxMcastPackets: 100, RxBcastPackets: 100, RxErrorPackets: 100, TxBytes: 100, TxPackets: 100, TxUcastPackets: 100, TxMcastPackets: 100, TxBcastPackets: 100, TxErrorPackets: 100, RxCrcErrors: 100, BipErrors: 100, Timestamp: 1000}}}}},
+ {"handleIndication-15", dh1, args{indication: &oop.Indication{Data: &oop.Indication_FlowStats{FlowStats: &oop.FlowStatistics{RxBytes: 100, RxPackets: 100, TxBytes: 100, TxPackets: 100, Timestamp: 1000}}}}},
+ {"handleIndication-16", dh1, args{indication: &oop.Indication{Data: &oop.Indication_AlarmInd{AlarmInd: &oop.AlarmIndication{}}}}},
+ {"handleIndication-17", dh1, args{indication: &oop.Indication{Data: &oop.Indication_PktInd{PktInd: &oop.PacketIndication{IntfType: "nni", FlowId: 1234, PortNo: 1}}}}},
+ {"handleIndication-18", dh1, args{indication: &oop.Indication{Data: &oop.Indication_PktInd{PktInd: &oop.PacketIndication{}}}}},
+
+ // Negative testcases
+ {"handleIndication-19", dh2, args{indication: &oop.Indication{Data: &oop.Indication_OltInd{OltInd: &oop.OltIndication{OperState: "up"}}}}},
+ {"handleIndication-20", dh2, args{indication: &oop.Indication{Data: &oop.Indication_OltInd{OltInd: &oop.OltIndication{OperState: "down"}}}}},
+ {"handleIndication-21", dh2, args{indication: &oop.Indication{Data: &oop.Indication_IntfInd{IntfInd: &oop.IntfIndication{IntfId: 1, OperState: "up"}}}}},
+ {"handleIndication-22", dh2, args{indication: &oop.Indication{Data: &oop.Indication_IntfInd{IntfInd: &oop.IntfIndication{IntfId: 1, OperState: "down"}}}}},
+ {"handleIndication-23", dh2, args{indication: &oop.Indication{Data: &oop.Indication_IntfOperInd{IntfOperInd: &oop.IntfOperIndication{Type: "nni", IntfId: 1, OperState: "up"}}}}},
+ {"handleIndication-24", dh2, args{indication: &oop.Indication{Data: &oop.Indication_IntfOperInd{IntfOperInd: &oop.IntfOperIndication{Type: "pon", IntfId: 1, OperState: "up"}}}}},
+ {"handleIndication-25", dh2, args{indication: &oop.Indication{Data: &oop.Indication_OnuDiscInd{OnuDiscInd: &oop.OnuDiscIndication{IntfId: 1, SerialNumber: &oop.SerialNumber{VendorId: []byte("TWSH"), VendorSpecific: []byte("1234")}}}}}},
+ {"handleIndication-26", dh2, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "up", AdminState: "up"}}}}},
+ {"handleIndication-27", dh2, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "up", AdminState: "down"}}}}},
+ {"handleIndication-28", dh2, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "down", AdminState: "up"}}}}},
+ {"handleIndication-29", dh2, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "down", AdminState: "down"}}}}},
+ {"handleIndication-30", dh2, args{indication: &oop.Indication{Data: &oop.Indication_OmciInd{OmciInd: &oop.OmciIndication{IntfId: 1, OnuId: 1, Pkt: []byte("onu123-random value")}}}}},
+ {"handleIndication-31", dh2, args{indication: &oop.Indication{Data: &oop.Indication_PktInd{PktInd: &oop.PacketIndication{IntfType: "nni", IntfId: 1, GemportId: 1, FlowId: 1234, PortNo: 1}}}}},
+ {"handleIndication-32", dh2, args{indication: &oop.Indication{Data: &oop.Indication_PortStats{PortStats: &oop.PortStatistics{IntfId: 1, RxBytes: 100, RxPackets: 100, RxUcastPackets: 100, RxMcastPackets: 100, RxBcastPackets: 100, RxErrorPackets: 100, TxBytes: 100, TxPackets: 100, TxUcastPackets: 100, TxMcastPackets: 100, TxBcastPackets: 100, TxErrorPackets: 100, RxCrcErrors: 100, BipErrors: 100, Timestamp: 1000}}}}},
+ {"handleIndication-33", dh2, args{indication: &oop.Indication{Data: &oop.Indication_FlowStats{FlowStats: &oop.FlowStatistics{RxBytes: 100, RxPackets: 100, TxBytes: 100, TxPackets: 100, Timestamp: 1000}}}}},
+ {"handleIndication-34", dh2, args{indication: &oop.Indication{Data: &oop.Indication_AlarmInd{AlarmInd: &oop.AlarmIndication{}}}}},
+ //
+ {"handleIndication-35", dh3, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "up", AdminState: "up"}}}}},
+ {"handleIndication-36", dh3, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "down", AdminState: "up"}}}}},
+ {"handleIndication-37", dh3, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "up", AdminState: "down"}}}}},
+ {"handleIndication-38", dh3, args{indication: &oop.Indication{Data: &oop.Indication_OnuInd{OnuInd: &oop.OnuIndication{IntfId: 1, OnuId: 1, OperState: "down", AdminState: "down"}}}}},
+ {"handleIndication-30", dh1, args{indication: &oop.Indication{Data: &oop.Indication_OmciInd{OmciInd: &oop.OmciIndication{IntfId: 1, OnuId: 4, Pkt: []byte("onu123-random value")}}}}},
+ {"handleIndication-30", dh2, args{indication: &oop.Indication{Data: &oop.Indication_OmciInd{OmciInd: &oop.OmciIndication{IntfId: 1, OnuId: 4, Pkt: []byte("onu123-random value")}}}}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ dh := tt.deviceHandler
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ dh.handleIndication(ctx, tt.args.indication)
+ })
+ }
+}
+
+func TestDeviceHandler_addPort(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ type args struct {
+ intfID uint32
+ portType voltha.Port_PortType
+ state string
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ }{
+ // State up
+ {"addPort.1", dh1, args{intfID: 1, portType: voltha.Port_UNKNOWN, state: "up"}},
+ {"addPort.2", dh1, args{intfID: 1, portType: voltha.Port_VENET_OLT, state: "up"}},
+ {"addPort.3", dh1, args{intfID: 1, portType: voltha.Port_VENET_ONU, state: "up"}},
+ {"addPort.4", dh1, args{intfID: 1, portType: voltha.Port_ETHERNET_NNI, state: "up"}},
+ {"addPort.5", dh1, args{intfID: 1, portType: voltha.Port_ETHERNET_UNI, state: "up"}},
+ {"addPort.6", dh1, args{intfID: 1, portType: voltha.Port_PON_OLT, state: "up"}},
+ {"addPort.7", dh1, args{intfID: 1, portType: voltha.Port_PON_ONU, state: "up"}},
+ {"addPort.8", dh1, args{intfID: 1, portType: 8, state: "up"}},
+ // state discovery
+ {"addPort.9", dh1, args{intfID: 1, portType: voltha.Port_UNKNOWN, state: "down"}},
+ {"addPort.10", dh1, args{intfID: 1, portType: voltha.Port_VENET_OLT, state: "down"}},
+ {"addPort.11", dh1, args{intfID: 1, portType: voltha.Port_VENET_ONU, state: "down"}},
+ {"addPort.12", dh1, args{intfID: 1, portType: voltha.Port_ETHERNET_NNI, state: "down"}},
+ {"addPort.13", dh1, args{intfID: 1, portType: voltha.Port_ETHERNET_UNI, state: "down"}},
+ {"addPort.14", dh1, args{intfID: 1, portType: voltha.Port_PON_OLT, state: "down"}},
+ {"addPort.15", dh1, args{intfID: 1, portType: voltha.Port_PON_ONU, state: "down"}},
+ {"addPort.16", dh1, args{intfID: 1, portType: 8, state: "down"}},
+
+ {"addPort.17", dh2, args{intfID: 1, portType: voltha.Port_ETHERNET_NNI, state: "up"}},
+ {"addPort.18", dh2, args{intfID: 1, portType: voltha.Port_ETHERNET_UNI, state: "up"}},
+ {"addPort.19", dh2, args{intfID: 1, portType: voltha.Port_ETHERNET_NNI, state: "down"}},
+ {"addPort.20", dh2, args{intfID: 1, portType: voltha.Port_ETHERNET_UNI, state: "down"}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tt.devicehandler.addPort(tt.args.intfID, tt.args.portType, tt.args.state)
+ })
+ }
+}
+
+func Test_macAddressToUint32Array(t *testing.T) {
+ type args struct {
+ mac string
+ }
+ tests := []struct {
+ name string
+ args args
+ want []uint32
+ }{
+ // TODO: Add test cases.
+ {"macAddressToUint32Array-1", args{mac: "00:00:00:00:00:01"}, []uint32{0, 0, 0, 0, 0, 1}},
+ {"macAddressToUint32Array-2", args{mac: "0abcdef"}, []uint32{11259375}},
+ {"macAddressToUint32Array-3", args{mac: "testing"}, []uint32{1, 2, 3, 4, 5, 6}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := macAddressToUint32Array(tt.args.mac); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("macAddressToUint32Array() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestDeviceHandler_handleOltIndication(t *testing.T) {
+
+ type args struct {
+ oltIndication *oop.OltIndication
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ {"handleOltIndication-1", args{oltIndication: &oop.OltIndication{OperState: "up"}}},
+ {"handleOltIndication-2", args{oltIndication: &oop.OltIndication{OperState: "down"}}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ dh := newMockDeviceHandler()
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ dh.handleOltIndication(ctx, tt.args.oltIndication)
+ })
+ }
+}
+
+func TestDeviceHandler_AdoptDevice(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ }{
+ // TODO: Add test cases.
+ {"AdoptDevice-1", dh1, args{device: dh1.device}},
+ {"AdoptDevice-2", dh2, args{device: dh2.device}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ //dh.doStateInit()
+ // context.
+ //dh.AdoptDevice(tt.args.device)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ tt.devicehandler.postInit(ctx)
+ })
+ }
+}
+
+func TestDeviceHandler_activateONU(t *testing.T) {
+ dh := newMockDeviceHandler()
+ dh1 := negativeDeviceHandler()
+ type args struct {
+ intfID uint32
+ onuID int64
+ serialNum *oop.SerialNumber
+ serialNumber string
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ }{
+ {"activateONU-1", dh, args{intfID: 1, onuID: 1, serialNum: &oop.SerialNumber{VendorId: []byte("onu1")}}},
+ {"activateONU-2", dh, args{intfID: 2, onuID: 2, serialNum: &oop.SerialNumber{VendorId: []byte("onu2")}}},
+ {"activateONU-3", dh1, args{intfID: 1, onuID: 1, serialNum: &oop.SerialNumber{VendorId: []byte("onu1")}}},
+ {"activateONU-4", dh1, args{intfID: 2, onuID: 2, serialNum: &oop.SerialNumber{VendorId: []byte("onu2")}}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ tt.devicehandler.activateONU(ctx, tt.args.intfID, tt.args.onuID,
+ tt.args.serialNum, tt.args.serialNumber)
+ })
+ }
+}
+
+func TestDeviceHandler_start(t *testing.T) {
+ dh := newMockDeviceHandler()
+ dh1 := negativeDeviceHandler()
+ dh.start(context.Background())
+ dh.stop(context.Background())
+
+ dh1.start(context.Background())
+ dh1.stop(context.Background())
+
+}
+
+func TestDeviceHandler_PacketOut(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ acts := []*ofp.OfpAction{
+ fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
+ fu.Output(1),
+ }
+ pktout := &ofp.OfpPacketOut{BufferId: 0, InPort: 1, Actions: acts, Data: []byte("AYDCAAAOAODsSE5TiMwCBwQA4OxITlIEBQUwLzUxBgIAFAgEMC81MQoJbG9jYWxob3N0EBwFAawbqqACAAAAoRAxLjMuNi4xLjQuMS40NDEz/gYAgMILAgD+GQCAwgkDAAAAAGQAAAAAAAAAAgICAgICAgL+GQCAwgoDAAAAAGQAAAAAAAAAAgICAgICAgIAAA==")}
+ type args struct {
+ egressPortNo int
+ packet *of.OfpPacketOut
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ //{"test1", args{egressPortNo: 0, packet: &ofp.OfpPacketOut{}}, true},
+ {"PacketOut-1", dh1, args{egressPortNo: 0, packet: pktout}, false},
+ {"PacketOut-2", dh2, args{egressPortNo: 1, packet: pktout}, false},
+ {"PacketOut-2", dh2, args{egressPortNo: 115000, packet: pktout}, false},
+ {"PacketOut-3", dh1, args{egressPortNo: 65536, packet: pktout}, false},
+ {"PacketOut-4", dh2, args{egressPortNo: 65535, packet: pktout}, false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ dh := tt.devicehandler
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := dh.PacketOut(ctx, tt.args.egressPortNo, tt.args.packet); (err != nil) != tt.wantErr {
+ t.Errorf("DeviceHandler.PacketOut() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+//
+func TestDeviceHandler_doStateUp(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := newMockDeviceHandler()
+
+ dh2.deviceID = ""
+ dh3 := negativeDeviceHandler()
+
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ wantErr bool
+ }{
+ {"dostateup-1", dh1, false},
+ {"dostateup-2", dh2, false},
+ {"dostateup-3", dh3, true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := tt.devicehandler.doStateUp(ctx); (err != nil) != tt.wantErr {
+ t.Logf("DeviceHandler.doStateUp() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+func TestDeviceHandler_doStateDown(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ dh3 := newMockDeviceHandler()
+ dh3.device.OperStatus = voltha.OperStatus_UNKNOWN
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ wantErr bool
+ }{
+ {"dostatedown-1", dh1, false},
+ {"dostatedown-2", dh2, true},
+ {"dostatedown-2", dh3, true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := tt.devicehandler.doStateDown(ctx); (err != nil) != tt.wantErr {
+ t.Logf("DeviceHandler.doStateDown() error = %v", err)
+ }
+ })
+ }
+}
+
+func TestDeviceHandler_GetOfpDeviceInfo(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"GetOfpDeviceInfo-1", dh1, args{dh1.device}, false},
+ {"GetOfpDeviceInfo-2", dh1, args{&voltha.Device{}}, false},
+ {"GetOfpDeviceInfo-3", dh2, args{dh1.device}, false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ dh := tt.devicehandler
+ _, err := dh.GetOfpDeviceInfo(tt.args.device)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("DeviceHandler.GetOfpDeviceInfo() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ })
+ }
+}
+
+func TestDeviceHandler_GetOfpPortInfo(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ type args struct {
+ device *voltha.Device
+ portNo int64
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ wantErr bool
+ }{
+ {"GetOfpPortInfo-1", dh1, args{device: dh1.device, portNo: 1}, false},
+ {"GetOfpPortInfo-2", dh2, args{device: dh2.device, portNo: 1}, false},
+ {"GetOfpPortInfo-3", dh1, args{device: dh1.device, portNo: 0}, false},
+ {"GetOfpPortInfo-4", dh2, args{device: dh2.device, portNo: 0}, false},
+ {"GetOfpPortInfo-5", dh1, args{device: &voltha.Device{}, portNo: 1}, false},
+ {"GetOfpPortInfo-6", dh2, args{device: &voltha.Device{}, portNo: 0}, false},
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ dh := tt.devicehandler
+ _, err := dh.GetOfpPortInfo(tt.args.device, tt.args.portNo)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("DeviceHandler.GetOfpPortInfo() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ })
+ }
+}
+
+func TestDeviceHandler_onuDiscIndication(t *testing.T) {
+
+ dh1 := newMockDeviceHandler()
+ dh1.discOnus = sync.Map{}
+ dh1.discOnus.Store("onu1", true)
+ dh1.discOnus.Store("onu2", false)
+ dh2 := negativeDeviceHandler()
+ type args struct {
+ onuDiscInd *oop.OnuDiscIndication
+ sn string
+ }
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ args args
+ }{
+ // TODO: Add test cases.
+ {"onuDiscIndication-1", dh1, args{onuDiscInd: &oop.OnuDiscIndication{IntfId: 1, SerialNumber: &oop.SerialNumber{VendorId: []byte("TWSH"), VendorSpecific: []byte("1234")}}}},
+ {"onuDiscIndication-2", dh1, args{onuDiscInd: &oop.OnuDiscIndication{IntfId: 1, SerialNumber: &oop.SerialNumber{}}}},
+ {"onuDiscIndication-3", dh1, args{onuDiscInd: &oop.OnuDiscIndication{SerialNumber: &oop.SerialNumber{}}}},
+ {"onuDiscIndication-4", dh1, args{onuDiscInd: &oop.OnuDiscIndication{}}},
+ {"onuDiscIndication-5", dh1, args{onuDiscInd: &oop.OnuDiscIndication{IntfId: 1, SerialNumber: &oop.SerialNumber{VendorId: []byte("TWSH"), VendorSpecific: []byte("1234")}}, sn: "onu1"}},
+ {"onuDiscIndication-6", dh1, args{onuDiscInd: &oop.OnuDiscIndication{IntfId: 1, SerialNumber: &oop.SerialNumber{VendorId: []byte("TWSH"), VendorSpecific: []byte("1234")}}, sn: "onu2"}},
+ {"onuDiscIndication-7", dh2, args{onuDiscInd: &oop.OnuDiscIndication{IntfId: 1, SerialNumber: &oop.SerialNumber{VendorId: []byte("TWSH"), VendorSpecific: []byte("1234")}}}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ tt.devicehandler.onuDiscIndication(ctx, tt.args.onuDiscInd, tt.args.sn)
+ })
+ }
+}
+
+func TestDeviceHandler_populateDeviceInfo(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := negativeDeviceHandler()
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"populateDeviceInfo-1", dh1, false},
+ {"populateDeviceInfo-2", dh1, true},
+ {"populateDeviceInfo-3", dh1, true},
+ {"populateDeviceInfo-4", dh1, true},
+ {"populateDeviceInfo-5", dh2, true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+
+ _, err := tt.devicehandler.populateDeviceInfo()
+ if (err != nil) != tt.wantErr {
+ t.Errorf("DeviceHandler.populateDeviceInfo() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+
+ })
+ }
+}
+
+func TestDeviceHandler_readIndications(t *testing.T) {
+ dh1 := newMockDeviceHandler()
+ dh2 := newMockDeviceHandler()
+ dh2.adminState = "down"
+ dh3 := newMockDeviceHandler()
+ dh3.device.AdminState = voltha.AdminState_DISABLED
+ dh4 := negativeDeviceHandler()
+ tests := []struct {
+ name string
+ devicehandler *DeviceHandler
+ }{
+ // TODO: Add test cases.
+ {"readIndications-1", dh1},
+ {"readIndications-2", dh2},
+ {"readIndications-3", dh2},
+ {"readIndications-4", dh2},
+ {"readIndications-5", dh2},
+ {"readIndications-6", dh3},
+ {"readIndications-7", dh3},
+ {"readIndications-8", dh4},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ tt.devicehandler.readIndications(ctx)
+ })
+ }
+}
+
+func Test_startCollector(t *testing.T) {
+ type args struct {
+ dh *DeviceHandler
+ }
+ dh := newMockDeviceHandler()
+ dh.portStats.NorthBoundPort = make(map[uint32]*NniPort)
+ dh.portStats.NorthBoundPort[0] = &NniPort{Name: "OLT-1"}
+ dh.portStats.SouthBoundPort = make(map[uint32]*PonPort)
+ dh.portStats.Device = dh
+ for i := 0; i < 16; i++ {
+ dh.portStats.SouthBoundPort[uint32(i)] = &PonPort{DeviceID: "OLT-1"}
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ // TODO: Add test cases.
+ {"StartCollector-1", args{dh}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ go func() {
+ time.Sleep(66 * time.Second) // startCollector inside waits for 1 min, so we stop it after 6 secs of running
+ tt.args.dh.stopCollector <- true
+ }()
+ startCollector(tt.args.dh)
+ })
+ }
+}
diff --git a/internal/pkg/core/error.go b/internal/pkg/core/error.go
new file mode 100644
index 0000000..0de7dc8
--- /dev/null
+++ b/internal/pkg/core/error.go
@@ -0,0 +1,328 @@
+/*
+ * 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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "strings"
+)
+
+const (
+ defaultLogAndReturnLevel = log.DebugLevel
+)
+
+func copy(src log.Fields) log.Fields {
+ dst := make(log.Fields)
+ for k, v := range src {
+ dst[k] = v
+ }
+ return dst
+}
+
+func merge(one, two log.Fields) log.Fields {
+ dst := make(log.Fields)
+ for k, v := range one {
+ dst[k] = v
+ }
+ for k, v := range two {
+ dst[k] = v
+ }
+ return dst
+}
+
+// LoggableError defined functions that can be used to log an object
+type LoggableError interface {
+ error
+ Log() error
+ LogAt(log.LogLevel) error
+}
+
+// ErrAdapter represents a basic adapter error that combines an name, field set
+// and wrapped error
+type ErrAdapter struct {
+ name string
+ fields log.Fields
+ wrapped error
+}
+
+// NewErrAdapter constructs a new error with the given values
+func NewErrAdapter(name string, fields log.Fields, wrapped error) LoggableError {
+ return &ErrAdapter{
+ name: name,
+ fields: copy(fields),
+ wrapped: wrapped,
+ }
+}
+
+// Name returns the error name
+func (e *ErrAdapter) Name() string {
+ return e.name
+}
+
+// Fields returns the fields associated with the error
+func (e *ErrAdapter) Fields() log.Fields {
+ return e.fields
+}
+
+// Unwrap returns the wrapped or nested error
+func (e *ErrAdapter) Unwrap() error {
+ return e.wrapped
+}
+
+// Error returns a string representation of the error
+func (e *ErrAdapter) Error() string {
+ var buf strings.Builder
+ buf.WriteString(e.name)
+ if len(e.fields) > 0 {
+ if val, err := json.Marshal(e.fields); err == nil {
+ buf.WriteString(": [")
+ buf.WriteString(string(val))
+ buf.WriteString("]")
+ }
+ }
+ if e.wrapped != nil {
+ buf.WriteString(": ")
+ buf.WriteString(e.wrapped.Error())
+ }
+ return buf.String()
+}
+
+// Log logs the error at the default level for log and return
+func (e *ErrAdapter) Log() error {
+ return e.LogAt(defaultLogAndReturnLevel)
+}
+
+// LogAt logs the error at the specified level and then returns the error
+func (e *ErrAdapter) LogAt(level log.LogLevel) error {
+ logger := log.Debugw
+ switch level {
+ case log.InfoLevel:
+ logger = log.Infow
+ case log.WarnLevel:
+ logger = log.Warnw
+ case log.ErrorLevel:
+ logger = log.Errorw
+ case log.FatalLevel:
+ logger = log.Fatalw
+ }
+ local := e.fields
+ if e.wrapped != nil {
+ local = merge(e.fields, log.Fields{"wrapped": e.wrapped})
+ }
+ logger(e.name, local)
+ return e
+}
+
+// ErrInvalidValue represents an error condition with given value is not able to
+// be processed
+type ErrInvalidValue struct {
+ ErrAdapter
+}
+
+// NewErrInvalidValue constructs a new error based on the given values
+func NewErrInvalidValue(fields log.Fields, wrapped error) LoggableError {
+ return &ErrInvalidValue{
+ ErrAdapter{
+ name: "invalid-value",
+ fields: copy(fields),
+ wrapped: wrapped,
+ },
+ }
+}
+
+// Log logs the error at the default level for log and return
+func (e *ErrInvalidValue) Log() error {
+ _ = e.ErrAdapter.Log()
+ return e
+}
+
+// LogAt logs the error at the specified level and then returns the error
+func (e *ErrInvalidValue) LogAt(level log.LogLevel) error {
+ _ = e.ErrAdapter.LogAt(level)
+ return e
+}
+
+// ErrNotFound represents an error condition when a value can not be located
+// given a field set of criteria
+type ErrNotFound struct {
+ ErrAdapter
+}
+
+// NewErrNotFound constructs a new error based on the given values
+func NewErrNotFound(target string, fields log.Fields, wrapped error) LoggableError {
+ return &ErrNotFound{
+ ErrAdapter{
+ name: "not-found",
+ fields: merge(fields, log.Fields{"target": target}),
+ wrapped: wrapped,
+ },
+ }
+}
+
+// Log logs the error at the default level for log and return
+func (e *ErrNotFound) Log() error {
+ _ = e.ErrAdapter.Log()
+ return e
+}
+
+// LogAt logs the error at the specified level and then returns the error
+func (e *ErrNotFound) LogAt(level log.LogLevel) error {
+ _ = e.ErrAdapter.LogAt(level)
+ return e
+}
+
+// ErrPersistence representation an error condition when a persistence operation
+// did not succeed
+type ErrPersistence struct {
+ ErrAdapter
+}
+
+// NewErrPersistence constructs a new error based on the given values
+func NewErrPersistence(operation, entityType string, ID uint32, fields log.Fields, wrapped error) LoggableError {
+ return &ErrPersistence{
+ ErrAdapter{
+ name: "unable-to-persist",
+ fields: merge(fields, log.Fields{
+ "operation": operation,
+ "entity-type": entityType,
+ "id": fmt.Sprintf("0x%x", ID)}),
+ wrapped: wrapped,
+ },
+ }
+}
+
+// Log logs the error at the default level for log and return
+func (e *ErrPersistence) Log() error {
+ _ = e.ErrAdapter.Log()
+ return e
+}
+
+// LogAt logs the error at the specified level and then returns the error
+func (e *ErrPersistence) LogAt(level log.LogLevel) error {
+ _ = e.ErrAdapter.LogAt(level)
+ return e
+}
+
+// ErrCommunication representation an error condition when an interprocess
+// message communication fails
+type ErrCommunication struct {
+ ErrAdapter
+}
+
+// NewErrCommunication constructs a new error based on the given values
+func NewErrCommunication(operation string, fields log.Fields, wrapped error) LoggableError {
+ return &ErrCommunication{
+ ErrAdapter{
+ name: "failed-communication",
+ fields: merge(fields, log.Fields{
+ "operation": operation}),
+ wrapped: wrapped,
+ },
+ }
+}
+
+// Log logs the error at the default level for log and return
+func (e *ErrCommunication) Log() error {
+ _ = e.ErrAdapter.Log()
+ return e
+}
+
+// LogAt logs the error at the specified level and then returns the error
+func (e *ErrCommunication) LogAt(level log.LogLevel) error {
+ _ = e.ErrAdapter.LogAt(level)
+ return e
+}
+
+// ErrFlowOp represents an error condition when a flow operation to a device did
+// not succeed
+type ErrFlowOp struct {
+ ErrAdapter
+}
+
+// NewErrFlowOp constructs a new error based on the given values
+func NewErrFlowOp(operation string, ID uint32, fields log.Fields, wrapped error) LoggableError {
+ return &ErrPersistence{
+ ErrAdapter{
+ name: "unable-to-perform-flow-operation",
+ fields: merge(fields, log.Fields{
+ "operation": operation,
+ "id": fmt.Sprintf("0x%x", ID)}),
+ wrapped: wrapped,
+ },
+ }
+}
+
+// Log logs the error at the default level for log and return
+func (e *ErrFlowOp) Log() error {
+ _ = e.ErrAdapter.Log()
+ return e
+}
+
+// LogAt logs the error at the specified level and then returns the error
+func (e *ErrFlowOp) LogAt(level log.LogLevel) error {
+ _ = e.ErrAdapter.LogAt(level)
+ return e
+}
+
+// ErrTimeout represents an error condition when the deadline for performing an
+// operation has been exceeded
+type ErrTimeout struct {
+ ErrAdapter
+}
+
+// NewErrTimeout constructs a new error based on the given values
+func NewErrTimeout(operation string, fields log.Fields, wrapped error) LoggableError {
+ return &ErrTimeout{
+ ErrAdapter{
+ name: "operation-timed-out",
+ fields: merge(fields, log.Fields{"operation": operation}),
+ wrapped: wrapped,
+ },
+ }
+}
+
+// Log logs the error at the default level for log and return
+func (e *ErrTimeout) Log() error {
+ _ = e.ErrAdapter.Log()
+ return e
+}
+
+// LogAt logs the error at the specified level and then returns the error
+func (e *ErrTimeout) LogAt(level log.LogLevel) error {
+ _ = e.ErrAdapter.LogAt(level)
+ return e
+}
+
+var (
+ // ErrNotImplemented error returned when an unimplemented method is
+ // invoked
+ ErrNotImplemented = NewErrAdapter("not-implemented", nil, nil)
+
+ // ErrInvalidPortRange error returned when a given port is not in the
+ // valid range
+ ErrInvalidPortRange = NewErrAdapter("invalid-port-range", nil, nil)
+
+ // ErrStateTransition error returned when a state transition is fails
+ ErrStateTransition = NewErrAdapter("state-transition", nil, nil)
+
+ // ErrResourceManagerInstantiating error returned when an unexpected
+ // condition occcurs while instantiating the resource manager
+ ErrResourceManagerInstantiating = NewErrAdapter("resoure-manager-instantiating", nil, nil)
+)
diff --git a/internal/pkg/core/olt_platform.go b/internal/pkg/core/olt_platform.go
new file mode 100644
index 0000000..747cccf
--- /dev/null
+++ b/internal/pkg/core/olt_platform.go
@@ -0,0 +1,276 @@
+/*
+ * 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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+/*=====================================================================
+
+@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 (
+ // Number of bits for the physical UNI of the ONUs
+ bitsForUniID = 4
+ // Number of bits for the ONU ID
+ bitsForONUID = 8
+ // Number of bits for PON ID
+ bitsForPONID = 8
+ // Number of bits to differentiate between UNI and NNI Logical Port
+ bitsForUNINNIDiff = 1
+ //MaxOnusPerPon is Max number of ONUs on any PON port
+ MaxOnusPerPon = (1 << bitsForONUID)
+ //MaxPonsPerOlt is Max number of PON ports on any OLT
+ MaxPonsPerOlt = (1 << bitsForPONID)
+ //MaxUnisPerOnu is the Max number of UNI ports on any ONU
+ 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)
+)
+
+//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(onuID)
+ if limit > MaxOnusPerPon {
+ log.Warn("Warning: exceeded the MAX ONUS per PON")
+ }
+ 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 {
+ log.Errorw("NNIPortNumber is not in valid range", log.Fields{"portNum": portNum})
+ return uint32(0), 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, NewErrNotFound("pon-interface", log.Fields{
+ "flow-direction": flowDirection}, nil)
+ }
+
+ ponIntf = IntfIDFromUniPortNum(uniPortNo)
+ onuID = OnuIDFromUniPortNum(uniPortNo)
+ uniID = UniIDFromPortNum(uniPortNo)
+
+ log.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/core/olt_platform_test.go b/internal/pkg/core/olt_platform_test.go
new file mode 100644
index 0000000..4f3d5bf
--- /dev/null
+++ b/internal/pkg/core/olt_platform_test.go
@@ -0,0 +1,383 @@
+/*
+ * 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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "math"
+ "reflect"
+ "testing"
+
+ fu "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+ ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+func TestMkUniPortNum(t *testing.T) {
+ type args struct {
+ intfID uint32
+ onuID uint32
+ uniID uint32
+ }
+ tests := []struct {
+ name string
+ args args
+ want uint32
+ }{
+ // TODO: Add test cases.
+ {"MkUniPortNum-1", args{1, 1, 1}, ((1 * 4096) + (1 * 16) + 1)},
+ {"MkUniPortNum-2", args{4, 5, 6}, ((4 * 4096) + (5 * 16) + 6)},
+ // Negative test cases to cover the log.warn
+ {"MkUniPortNum-3", args{4, 130, 6}, ((4 * 4096) + (130 * 16) + 6)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := MkUniPortNum(tt.args.intfID, tt.args.onuID, tt.args.uniID); got != tt.want {
+ t.Errorf("MkUniPortNum() = %v, want %v", got, tt.want)
+ } else {
+ t.Logf("Expected %v , Actual %v \n", tt.want, got)
+ }
+ })
+ }
+}
+
+func TestOnuIDFromPortNum(t *testing.T) {
+ type args struct {
+ portNum uint32
+ }
+ tests := []struct {
+ name string
+ args args
+ want uint32
+ }{
+ // TODO: Add test cases.
+ {"OnuIDFromPortNum-1", args{portNum: 8096}, ((8096 / 16) & 255)},
+ {"OnuIDFromPortNum-2", args{portNum: 9095}, ((9095 / 16) & 255)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := OnuIDFromPortNum(tt.args.portNum); got != tt.want {
+ t.Errorf("OnuIDFromPortNum() = %v, want %v", got, tt.want)
+ } else {
+ t.Logf("Expected %v , Actual %v \n", tt.want, got)
+ }
+ })
+ }
+}
+
+func TestIntfIDFromUniPortNum(t *testing.T) {
+ type args struct {
+ portNum uint32
+ }
+ tests := []struct {
+ name string
+ args args
+ want uint32
+ }{
+ // TODO: Add test cases.
+ {"IntfIDFromUniPortNum-1", args{portNum: 8096}, ((8096 / 4096) & 15)},
+ // Negative Testcase
+ {"IntfIDFromUniPortNum-2", args{portNum: 1024}, ((1024 / 4096) & 15)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := IntfIDFromUniPortNum(tt.args.portNum); got != tt.want {
+ t.Errorf("IntfIDFromUniPortNum() = %v, want %v", got, tt.want)
+ } else {
+ t.Logf("Expected %v , Actual %v \n", tt.want, got)
+ }
+ })
+ }
+}
+
+func TestUniIDFromPortNum(t *testing.T) {
+ type args struct {
+ portNum uint32
+ }
+ tests := []struct {
+ name string
+ args args
+ want uint32
+ }{
+
+ // TODO: Add test cases.
+ {"UniIDFromPortNum-1", args{portNum: 8096}, (8096 & 15)},
+ {"UniIDFromPortNum-2", args{portNum: 1024}, (1024 & 15)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := UniIDFromPortNum(tt.args.portNum); got != tt.want {
+ t.Errorf("UniIDFromPortNum() = %v, want %v", got, tt.want)
+ } else {
+ t.Logf("Expected %v , Actual %v \n", tt.want, got)
+ }
+ })
+ }
+}
+
+func TestIntfIDToPortNo(t *testing.T) {
+ type args struct {
+ intfID uint32
+ intfType voltha.Port_PortType
+ }
+ tests := []struct {
+ name string
+ args args
+ want uint32
+ }{
+ // TODO: Add test cases.
+ {"IntfIDToPortNo-1", args{intfID: 120, intfType: voltha.Port_ETHERNET_NNI}, (uint32(math.Pow(2, 20)) + 120)},
+ {"IntfIDToPortNo-2", args{intfID: 1024, intfType: voltha.Port_ETHERNET_UNI}, 0},
+ {"IntfIDToPortNo-3", args{intfID: 456, intfType: voltha.Port_PON_OLT}, (uint32(2*math.Pow(2, 28)) + 456)},
+ {"IntfIDToPortNo-4", args{intfID: 28, intfType: voltha.Port_PON_ONU}, 0},
+ {"IntfIDToPortNo-5", args{intfID: 45, intfType: voltha.Port_UNKNOWN}, 0},
+ {"IntfIDToPortNo-6", args{intfID: 45, intfType: voltha.Port_VENET_OLT}, 0},
+ {"IntfIDToPortNo-7", args{intfID: 45, intfType: voltha.Port_VENET_ONU}, 0},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := IntfIDToPortNo(tt.args.intfID, tt.args.intfType); got != tt.want {
+ t.Errorf("IntfIDToPortNo() = %v, want %v", got, tt.want)
+ } else {
+ t.Logf("Expected %v , Actual %v \n", tt.want, got)
+ }
+ })
+ }
+}
+
+func TestIntfIDFromNniPortNum(t *testing.T) {
+ type args struct {
+ portNum uint32
+ }
+
+ tests := []struct {
+ name string
+ args args
+ want uint32
+ wantErr error
+ }{
+ // TODO: Add test cases.
+ {"IntfIDFromNniPortNum-01", args{portNum: 8081}, 0, ErrInvalidPortRange},
+ {"IntfIDFromNniPortNum-02", args{portNum: 9090}, 0, ErrInvalidPortRange},
+ {"IntfIDFromNniPortNum-03", args{portNum: 0}, 0, ErrInvalidPortRange},
+ {"IntfIDFromNniPortNum-04", args{portNum: 65535}, 0, ErrInvalidPortRange},
+ {"IntfIDFromNniPortNum-05", args{portNum: 1048575}, 0, ErrInvalidPortRange},
+ {"IntfIDFromNniPortNum-06", args{portNum: 1048576}, 0, nil},
+ {"IntfIDFromNniPortNum-07", args{portNum: 1048577}, 1, nil},
+ {"IntfIDFromNniPortNum-08", args{portNum: 1048578}, 2, nil},
+ {"IntfIDFromNniPortNum-09", args{portNum: 1048579}, 3, nil},
+ {"IntfIDFromNniPortNum-10", args{portNum: 2097150}, 65534, nil},
+ {"IntfIDFromNniPortNum-11", args{portNum: 2097151}, 65535, nil},
+ {"IntfIDFromNniPortNum-12", args{portNum: 3000000}, 0, ErrInvalidPortRange},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := IntfIDFromNniPortNum(tt.args.portNum)
+ if got != tt.want || err != tt.wantErr {
+ t.Errorf("IntfIDFromNniPortNum(): FOR[%v] WANT[%v and %v] GOT[%v and %v]",
+ tt.args.portNum, tt.want, tt.wantErr, got, err)
+ }
+ })
+ }
+}
+
+func TestIntfIDToPortTypeName(t *testing.T) {
+ type args struct {
+ intfID uint32
+ }
+ var input uint32
+ input = uint32(2*math.Pow(2, 28)) | 3
+ tests := []struct {
+ name string
+ args args
+ want voltha.Port_PortType
+ }{
+ // TODO: Add test cases.
+ {"IntfIDToPortTypeName-1", args{intfID: 1048576}, voltha.Port_ETHERNET_NNI},
+ {"IntfIDToPortTypeName-2", args{intfID: 1000}, voltha.Port_ETHERNET_UNI},
+ {"IntfIDToPortTypeName-2", args{intfID: input}, voltha.Port_PON_OLT},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := IntfIDToPortTypeName(tt.args.intfID); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("IntfIDToPortTypeName() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestExtractAccessFromFlow(t *testing.T) {
+ type args struct {
+ inPort uint32
+ outPort uint32
+ }
+ tests := []struct {
+ name string
+ args args
+ port uint32
+ IntfID uint32
+ onuID uint32
+ uniID uint32
+ }{
+ // TODO: Add test cases.
+ {"ExtractAccessFromFlow-1", args{inPort: 100, outPort: 1048576}, 100, 0, 6, 4},
+ {"ExtractAccessFromFlow-2", args{inPort: 1048576, outPort: 10}, 10, 0, 0, 10},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, got1, got2, got3 := ExtractAccessFromFlow(tt.args.inPort, tt.args.outPort)
+ if got != tt.port {
+ t.Errorf("ExtractAccessFromFlow() got = %v, want %v", got, tt.port)
+ }
+ if got1 != tt.IntfID {
+ t.Errorf("ExtractAccessFromFlow() got1 = %v, want %v", got1, tt.IntfID)
+ }
+ if got2 != tt.onuID {
+ t.Errorf("ExtractAccessFromFlow() got2 = %v, want %v", got2, tt.onuID)
+ }
+ if got3 != tt.uniID {
+ t.Errorf("ExtractAccessFromFlow() got3 = %v, want %v", got3, tt.uniID)
+ }
+ })
+ }
+ //t.Error()
+}
+
+func TestIsUpstream(t *testing.T) {
+ type args struct {
+ outPort uint32
+ }
+ tests := []struct {
+ name string
+ args args
+ want bool
+ }{
+ // TODO: Add test cases.
+ {"TestIsUpstream-1", args{outPort: 65533}, true},
+ {"TestIsUpstream-2", args{outPort: 1048576}, true},
+ {"TestIsUpstream-3", args{outPort: 1048577}, true},
+ {"TestIsUpstream-4", args{outPort: 1048578}, true},
+ {"TestIsUpstream-6", args{outPort: 1000}, false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := IsUpstream(tt.args.outPort); got != tt.want {
+ t.Errorf("IsUpstream() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestIsControllerBoundFlow(t *testing.T) {
+ type args struct {
+ outPort uint32
+ }
+ tests := []struct {
+ name string
+ args args
+ want bool
+ }{
+ // TODO: Add test cases.
+ {"IsControllerBoundFlow-1", args{outPort: 65533}, true},
+ {"IsControllerBoundFlow-2", args{outPort: 65536}, false},
+ {"IsControllerBoundFlow-3", args{outPort: 65537}, false},
+ {"IsControllerBoundFlow-4", args{outPort: 65538}, false},
+ {"IsControllerBoundFlow-5", args{outPort: 65539}, false},
+ {"IsControllerBoundFlow-6", args{outPort: 1000}, false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := IsControllerBoundFlow(tt.args.outPort); got != tt.want {
+ t.Errorf("IsControllerBoundFlow() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestFlowExtractInfo(t *testing.T) {
+ fa := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(2),
+ fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2)),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ fu.EthType(2048),
+ },
+
+ Actions: []*ofp.OfpAction{
+ fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
+ fu.Output(1),
+ },
+ }
+ ofpstats := fu.MkFlowStat(fa)
+ type args struct {
+ flow *ofp.OfpFlowStats
+ flowDirection string
+ }
+ tests := []struct {
+ name string
+ args args
+ want uint32
+ want1 uint32
+ want2 uint32
+ want3 uint32
+ want4 uint32
+ want5 uint32
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"FlowExtractInfo-1", args{flow: ofpstats, flowDirection: "upstream"}, 2, 0, 0, 2, 0, 0, false},
+
+ // Negative Testcases
+ {"FlowExtractInfo-2", args{flow: ofpstats, flowDirection: "downstream"}, 1, 0, 0, 1, 2, 2048, false},
+ {"FlowExtractInfo-3", args{flow: nil, flowDirection: "downstream"}, 0, 0, 0, 0, 0, 0, true},
+ {"FlowExtractInfo-4", args{flow: &ofp.OfpFlowStats{}, flowDirection: "downstream"}, 0, 0, 0, 0, 0, 0, true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, got1, got2, got3, got4, got5, err := FlowExtractInfo(tt.args.flow, tt.args.flowDirection)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("FlowExtractInfo() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("FlowExtractInfo() got = %v, want %v", got, tt.want)
+ return
+ }
+ if got1 != tt.want1 {
+ t.Errorf("FlowExtractInfo() got1 = %v, want %v", got1, tt.want1)
+ return
+ }
+ if got2 != tt.want2 {
+ t.Errorf("FlowExtractInfo() got2 = %v, want %v", got2, tt.want2)
+ return
+ }
+ if got3 != tt.want3 {
+ t.Errorf("FlowExtractInfo() got3 = %v, want %v", got3, tt.want3)
+ return
+ }
+ if got4 != tt.want4 {
+ t.Errorf("FlowExtractInfo() got4 = %v, want %v", got4, tt.want4)
+ return
+ }
+ if got5 != tt.want5 {
+ t.Errorf("FlowExtractInfo() got5 = %v, want %v", got5, tt.want5)
+ return
+ }
+ })
+ }
+}
diff --git a/internal/pkg/core/olt_state_transitions.go b/internal/pkg/core/olt_state_transitions.go
new file mode 100644
index 0000000..c10d17d
--- /dev/null
+++ b/internal/pkg/core/olt_state_transitions.go
@@ -0,0 +1,187 @@
+/*
+ * 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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "context"
+ "reflect"
+ "runtime"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+// DeviceState OLT Device state
+type DeviceState int
+
+const (
+ // deviceStateNull OLT is not instantiated
+ deviceStateNull DeviceState = iota
+ // deviceStateInit OLT is instantiated
+ deviceStateInit
+ // deviceStateConnected Grpc session established with OLT
+ deviceStateConnected
+ // deviceStateUp Admin state of OLT is UP
+ deviceStateUp
+ // deviceStateDown Admin state of OLT is down
+ deviceStateDown
+)
+
+// Trigger for changing the state
+type Trigger int
+
+const (
+ // DeviceInit Go to Device init state
+ DeviceInit Trigger = iota
+ // GrpcConnected Go to connected state
+ GrpcConnected
+ // DeviceUpInd Go to Device up state
+ DeviceUpInd
+ // DeviceDownInd Go to Device down state
+ DeviceDownInd
+ // GrpcDisconnected Go to Device init state
+ GrpcDisconnected
+)
+
+// TransitionHandler function type for handling transition
+type TransitionHandler func(ctx context.Context) error
+
+// Transition to store state machine
+type Transition struct {
+ previousState []DeviceState
+ currentState DeviceState
+ before []TransitionHandler
+ after []TransitionHandler
+}
+
+// TransitionMap to store all the states and current device state
+type TransitionMap struct {
+ transitions map[Trigger]Transition
+ currentDeviceState DeviceState
+}
+
+// OpenoltDevice state machine:
+//
+// null ----> init ------> connected -----> up -----> down
+// ^ ^ | ^ | |
+// | | | | | |
+// | +-------------+ +---------+ |
+// | |
+// +-----------------------------------------+
+
+// NewTransitionMap create a new state machine with all the transitions
+func NewTransitionMap(dh *DeviceHandler) *TransitionMap {
+ var transitionMap TransitionMap
+ transitionMap.currentDeviceState = deviceStateNull
+ transitionMap.transitions = make(map[Trigger]Transition)
+ // In doInit establish the grpc session
+ transitionMap.transitions[DeviceInit] =
+ Transition{
+ previousState: []DeviceState{deviceStateNull, deviceStateDown},
+ currentState: deviceStateInit,
+ before: []TransitionHandler{dh.doStateInit},
+ after: []TransitionHandler{dh.postInit}}
+ // If gRpc session fails, re-establish the grpc session
+ transitionMap.transitions[GrpcDisconnected] =
+ Transition{
+ previousState: []DeviceState{deviceStateConnected, deviceStateDown},
+ currentState: deviceStateInit,
+ before: []TransitionHandler{dh.doStateInit},
+ after: []TransitionHandler{dh.postInit}}
+ // in doConnected, create logical device and read the indications
+ transitionMap.transitions[GrpcConnected] =
+ Transition{
+ previousState: []DeviceState{deviceStateInit},
+ currentState: deviceStateConnected,
+ before: []TransitionHandler{dh.doStateConnected}}
+
+ // Once the olt UP is indication received, then do state up
+ transitionMap.transitions[DeviceUpInd] =
+ Transition{
+ previousState: []DeviceState{deviceStateConnected, deviceStateDown},
+ currentState: deviceStateUp,
+ before: []TransitionHandler{dh.doStateUp}}
+ // If olt DOWN indication comes then do sate down
+ transitionMap.transitions[DeviceDownInd] =
+ Transition{
+ previousState: []DeviceState{deviceStateUp},
+ currentState: deviceStateDown,
+ before: []TransitionHandler{dh.doStateDown}}
+
+ return &transitionMap
+}
+
+// funcName gets the handler function name
+func funcName(f interface{}) string {
+ p := reflect.ValueOf(f).Pointer()
+ rf := runtime.FuncForPC(p)
+ return rf.Name()
+}
+
+// isValidTransition checks for the new state transition is valid from current state
+func (tMap *TransitionMap) isValidTransition(trigger Trigger) bool {
+ // Validate the state transition
+ for _, state := range tMap.transitions[trigger].previousState {
+ if tMap.currentDeviceState == state {
+ return true
+ }
+ }
+ return false
+}
+
+// Handle moves the state machine to next state based on the trigger and invokes the before and
+// after handlers if the transition is a valid transition
+func (tMap *TransitionMap) Handle(ctx context.Context, trigger Trigger) {
+
+ // Check whether the transtion is valid from current state
+ if !tMap.isValidTransition(trigger) {
+ log.Errorw("Invalid transition triggered ", log.Fields{"CurrentState": tMap.currentDeviceState, "Trigger": trigger})
+ return
+ }
+
+ // Invoke the before handlers
+ beforeHandlers := tMap.transitions[trigger].before
+ if beforeHandlers == nil {
+ log.Debugw("No handlers for before", log.Fields{"trigger": trigger})
+ }
+ for _, handler := range beforeHandlers {
+ log.Debugw("running-before-handler", log.Fields{"handler": funcName(handler)})
+ if err := handler(ctx); err != nil {
+ // TODO handle error
+ log.Error(err)
+ return
+ }
+ }
+
+ // Update the state
+ tMap.currentDeviceState = tMap.transitions[trigger].currentState
+ log.Debugw("Updated device state ", log.Fields{"CurrentDeviceState": tMap.currentDeviceState})
+
+ // Invoke the after handlers
+ afterHandlers := tMap.transitions[trigger].after
+ if afterHandlers == nil {
+ log.Debugw("No handlers for after", log.Fields{"trigger": trigger})
+ }
+ for _, handler := range afterHandlers {
+ log.Debugw("running-after-handler", log.Fields{"handler": funcName(handler)})
+ if err := handler(ctx); err != nil {
+ // TODO handle error
+ log.Error(err)
+ return
+ }
+ }
+}
diff --git a/internal/pkg/core/olt_state_transitions_test.go b/internal/pkg/core/olt_state_transitions_test.go
new file mode 100644
index 0000000..75e7807
--- /dev/null
+++ b/internal/pkg/core/olt_state_transitions_test.go
@@ -0,0 +1,188 @@
+/*
+ * 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 core
+
+import (
+ "context"
+ "reflect"
+ "testing"
+ "time"
+)
+
+/**
+Get's the transition Map with current state of the device.
+*/
+func getTranisitions() map[Trigger]Transition {
+ transitions := make(map[Trigger]Transition)
+ transition := Transition{
+ previousState: []DeviceState{deviceStateConnected},
+ currentState: deviceStateConnected,
+ }
+ transitions[DeviceInit] = transition
+ return transitions
+}
+
+/**
+Get's the transition Map with after Transition func added.
+*/
+func getTranisitionsAfter() map[Trigger]Transition {
+ transitions := make(map[Trigger]Transition)
+ transition := Transition{
+ previousState: []DeviceState{deviceStateConnected},
+ currentState: deviceStateConnected,
+ after: []TransitionHandler{func(ctx context.Context) error {
+ return nil
+ }, func(ctx context.Context) error {
+ return ErrStateTransition
+ }},
+ }
+ transitions[GrpcConnected] = transition
+ return transitions
+}
+
+/**
+Get's the transition Map with before Transition func added.
+*/
+func getTranisitionsBefore() map[Trigger]Transition {
+ transitions := make(map[Trigger]Transition)
+ transition := Transition{
+ previousState: []DeviceState{deviceStateConnected},
+ currentState: deviceStateConnected,
+ before: []TransitionHandler{func(ctx context.Context) error {
+ return nil
+ }, func(ctx context.Context) error {
+ return ErrStateTransition
+ }},
+ }
+ transitions[GrpcConnected] = transition
+ return transitions
+}
+
+/**
+Check's Creation of transition Map, return's NewTransitionMap.
+*/
+func TestNewTransitionMap(t *testing.T) {
+ type args struct {
+ dh *DeviceHandler
+ }
+ tests := []struct {
+ name string
+ args args
+ want *TransitionMap
+ }{
+ {"NewTransitionMap-1", args{newMockDeviceHandler()}, &TransitionMap{}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := NewTransitionMap(tt.args.dh); reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("NewTransitionMap() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+/**
+Checks the different transition of the device handled properly.
+*/
+func TestTransitionMap_Handle(t *testing.T) {
+ type fields struct {
+ transitions map[Trigger]Transition
+ currentDeviceState DeviceState
+ }
+ type args struct {
+ trigger Trigger
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ }{
+ {"Handle-1", fields{getTranisitions(), deviceStateDown}, args{GrpcConnected}},
+ {"Handle-2", fields{getTranisitions(), deviceStateConnected}, args{GrpcConnected}},
+ {"Handle-3", fields{getTranisitionsBefore(), deviceStateConnected}, args{GrpcConnected}},
+ {"Handle-4", fields{getTranisitionsAfter(), deviceStateConnected}, args{GrpcConnected}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tMap := &TransitionMap{
+ transitions: tt.fields.transitions,
+ currentDeviceState: tt.fields.currentDeviceState,
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ tMap.Handle(ctx, tt.args.trigger)
+ })
+ }
+}
+
+/**
+Check's if the transition is valid or not.
+*/
+func TestTransitionMap_isValidTransition(t *testing.T) {
+ type fields struct {
+ transitions map[Trigger]Transition
+ currentDeviceState DeviceState
+ }
+ type args struct {
+ trigger Trigger
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want bool
+ }{
+ {"isValidTransition-1", fields{getTranisitions(), deviceStateConnected}, args{DeviceInit},
+ true},
+ {"isValidTransition-2", fields{getTranisitions(), deviceStateDown}, args{GrpcConnected},
+ false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tMap := &TransitionMap{
+ transitions: tt.fields.transitions,
+ currentDeviceState: tt.fields.currentDeviceState,
+ }
+ if got := tMap.isValidTransition(tt.args.trigger); reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("isValidTransition() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+/**
+Get's the After/Before transition method's function name.
+*/
+func Test_funcName(t *testing.T) {
+ type args struct {
+ f interface{}
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ {"FuncName-1", args{newMockDeviceHandler()}, ""},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := funcName(tt.args.f); got != tt.want {
+ t.Errorf("funcName() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/internal/pkg/core/openolt.go b/internal/pkg/core/openolt.go
new file mode 100644
index 0000000..9a5167f
--- /dev/null
+++ b/internal/pkg/core/openolt.go
@@ -0,0 +1,388 @@
+/*
+ * 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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "context"
+ "sync"
+ "time"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+ "github.com/opencord/voltha-lib-go/v3/pkg/kafka"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/config"
+ 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"
+)
+
+//OpenOLT structure holds the OLT information
+type OpenOLT struct {
+ deviceHandlers map[string]*DeviceHandler
+ coreProxy adapterif.CoreProxy
+ adapterProxy adapterif.AdapterProxy
+ eventProxy adapterif.EventProxy
+ kafkaICProxy kafka.InterContainerProxy
+ config *config.AdapterFlags
+ numOnus int
+ KVStoreHost string
+ KVStorePort int
+ KVStoreType string
+ exitChannel chan int
+ HeartbeatCheckInterval time.Duration
+ HeartbeatFailReportInterval time.Duration
+ GrpcTimeoutInterval time.Duration
+ lockDeviceHandlersMap sync.RWMutex
+}
+
+//NewOpenOLT returns a new instance of OpenOLT
+func NewOpenOLT(ctx context.Context, kafkaICProxy kafka.InterContainerProxy,
+ coreProxy adapterif.CoreProxy, adapterProxy adapterif.AdapterProxy,
+ eventProxy adapterif.EventProxy, cfg *config.AdapterFlags) *OpenOLT {
+ var openOLT OpenOLT
+ openOLT.exitChannel = make(chan int, 1)
+ openOLT.deviceHandlers = make(map[string]*DeviceHandler)
+ openOLT.kafkaICProxy = kafkaICProxy
+ openOLT.config = cfg
+ openOLT.numOnus = cfg.OnuNumber
+ openOLT.coreProxy = coreProxy
+ openOLT.adapterProxy = adapterProxy
+ openOLT.eventProxy = eventProxy
+ openOLT.KVStoreHost = cfg.KVStoreHost
+ openOLT.KVStorePort = cfg.KVStorePort
+ openOLT.KVStoreType = cfg.KVStoreType
+ openOLT.HeartbeatCheckInterval = cfg.HeartbeatCheckInterval
+ openOLT.HeartbeatFailReportInterval = cfg.HeartbeatFailReportInterval
+ openOLT.GrpcTimeoutInterval = cfg.GrpcTimeoutInterval
+ openOLT.lockDeviceHandlersMap = sync.RWMutex{}
+ return &openOLT
+}
+
+//Start starts (logs) the device manager
+func (oo *OpenOLT) Start(ctx context.Context) error {
+ log.Info("starting-device-manager")
+ log.Info("device-manager-started")
+ return nil
+}
+
+//Stop terminates the session
+func (oo *OpenOLT) Stop(ctx context.Context) error {
+ log.Info("stopping-device-manager")
+ oo.exitChannel <- 1
+ log.Info("device-manager-stopped")
+ return nil
+}
+
+func sendResponse(ctx context.Context, ch chan interface{}, result interface{}) {
+ if ctx.Err() == nil {
+ // Returned response only of the ctx has not been canceled/timeout/etc
+ // Channel is automatically closed when a context is Done
+ ch <- result
+ log.Debugw("sendResponse", log.Fields{"result": result})
+ } else {
+ // Should the transaction be reverted back?
+ log.Debugw("sendResponse-context-error", log.Fields{"context-error": ctx.Err()})
+ }
+}
+
+func (oo *OpenOLT) addDeviceHandlerToMap(agent *DeviceHandler) {
+ oo.lockDeviceHandlersMap.Lock()
+ defer oo.lockDeviceHandlersMap.Unlock()
+ if _, exist := oo.deviceHandlers[agent.deviceID]; !exist {
+ oo.deviceHandlers[agent.deviceID] = agent
+ }
+}
+
+func (oo *OpenOLT) deleteDeviceHandlerToMap(agent *DeviceHandler) {
+ oo.lockDeviceHandlersMap.Lock()
+ defer oo.lockDeviceHandlersMap.Unlock()
+ delete(oo.deviceHandlers, agent.deviceID)
+}
+
+func (oo *OpenOLT) getDeviceHandler(deviceID string) *DeviceHandler {
+ oo.lockDeviceHandlersMap.Lock()
+ defer oo.lockDeviceHandlersMap.Unlock()
+ if agent, ok := oo.deviceHandlers[deviceID]; ok {
+ return agent
+ }
+ return nil
+}
+
+//createDeviceTopic returns
+func (oo *OpenOLT) createDeviceTopic(device *voltha.Device) error {
+ log.Infow("create-device-topic", log.Fields{"deviceId": device.Id})
+ defaultTopic := oo.kafkaICProxy.GetDefaultTopic()
+ deviceTopic := kafka.Topic{Name: defaultTopic.Name + "_" + device.Id}
+ // TODO for the offset
+ if err := oo.kafkaICProxy.SubscribeWithDefaultRequestHandler(deviceTopic, 0); err != nil {
+ log.Infow("create-device-topic-failed", log.Fields{"deviceId": device.Id, "error": err})
+ return err
+ }
+ return nil
+}
+
+// Adopt_device creates a new device handler if not present already and then adopts the device
+func (oo *OpenOLT) Adopt_device(device *voltha.Device) error {
+ ctx := context.Background()
+ if device == nil {
+ return NewErrInvalidValue(log.Fields{"device": nil}, nil).Log()
+ }
+ log.Infow("adopt-device", log.Fields{"deviceId": 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(handler)
+ go handler.AdoptDevice(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 *OpenOLT) Get_ofp_device_info(device *voltha.Device) (*ic.SwitchCapability, error) {
+ log.Infow("Get_ofp_device_info", log.Fields{"deviceId": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ return handler.GetOfpDeviceInfo(device)
+ }
+ return nil, NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil).Log()
+}
+
+//Get_ofp_port_info returns OFP port information for the given device
+func (oo *OpenOLT) Get_ofp_port_info(device *voltha.Device, portNo int64) (*ic.PortCapability, error) {
+ log.Infow("Get_ofp_port_info", log.Fields{"deviceId": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ return handler.GetOfpPortInfo(device, portNo)
+ }
+ return nil, NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil).Log()
+}
+
+//Process_inter_adapter_message sends messages to a target device (between adapters)
+func (oo *OpenOLT) Process_inter_adapter_message(msg *ic.InterAdapterMessage) error {
+ log.Infow("Process_inter_adapter_message", log.Fields{"msgId": msg.Header.Id})
+ targetDevice := msg.Header.ProxyDeviceId // Request?
+ if targetDevice == "" && msg.Header.ToDeviceId != "" {
+ // Typical response
+ targetDevice = msg.Header.ToDeviceId
+ }
+ if handler := oo.getDeviceHandler(targetDevice); handler != nil {
+ return handler.ProcessInterAdapterMessage(msg)
+ }
+ return NewErrNotFound("device-handler", log.Fields{"device-id": targetDevice}, nil).Log()
+}
+
+//Adapter_descriptor not implemented
+func (oo *OpenOLT) Adapter_descriptor() error {
+ return ErrNotImplemented
+}
+
+//Device_types unimplemented
+func (oo *OpenOLT) Device_types() (*voltha.DeviceTypes, error) {
+ return nil, ErrNotImplemented
+}
+
+//Health returns unimplemented
+func (oo *OpenOLT) Health() (*voltha.HealthStatus, error) {
+ return nil, ErrNotImplemented
+}
+
+//Reconcile_device unimplemented
+func (oo *OpenOLT) Reconcile_device(device *voltha.Device) error {
+ ctx := context.Background()
+ if device == nil {
+ return NewErrInvalidValue(log.Fields{"device": nil}, nil).Log()
+ }
+ log.Infow("reconcile-device", log.Fields{"deviceId": 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(handler)
+ handler.transitionMap = NewTransitionMap(handler)
+ handler.transitionMap.Handle(ctx, DeviceInit)
+ }
+ return nil
+}
+
+//Abandon_device unimplemented
+func (oo *OpenOLT) Abandon_device(device *voltha.Device) error {
+ return ErrNotImplemented
+}
+
+//Disable_device disables the given device
+func (oo *OpenOLT) Disable_device(device *voltha.Device) error {
+ log.Infow("disable-device", log.Fields{"deviceId": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ return handler.DisableDevice(device)
+ }
+ return NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil).Log()
+}
+
+//Reenable_device enables the olt device after disable
+func (oo *OpenOLT) Reenable_device(device *voltha.Device) error {
+ log.Infow("reenable-device", log.Fields{"deviceId": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ return handler.ReenableDevice(device)
+ }
+ return NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil).Log()
+}
+
+//Reboot_device reboots the given device
+func (oo *OpenOLT) Reboot_device(device *voltha.Device) error {
+ log.Infow("reboot-device", log.Fields{"deviceId": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ return handler.RebootDevice(device)
+ }
+ return NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil).Log()
+}
+
+//Self_test_device unimplented
+func (oo *OpenOLT) Self_test_device(device *voltha.Device) error {
+ return ErrNotImplemented
+}
+
+//Delete_device unimplemented
+func (oo *OpenOLT) Delete_device(device *voltha.Device) error {
+ log.Infow("delete-device", log.Fields{"deviceId": device.Id})
+ ctx := context.Background()
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ if err := handler.DeleteDevice(ctx, device); err != nil {
+ log.Errorw("failed-to-handle-delete-device", log.Fields{"device-id": device.Id})
+ }
+ oo.deleteDeviceHandlerToMap(handler)
+ return nil
+ }
+ return NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil).Log()
+}
+
+//Get_device_details unimplemented
+func (oo *OpenOLT) Get_device_details(device *voltha.Device) error {
+ return ErrNotImplemented
+}
+
+//Update_flows_bulk returns
+func (oo *OpenOLT) Update_flows_bulk(device *voltha.Device, flows *voltha.Flows, groups *voltha.FlowGroups, flowMetadata *voltha.FlowMetadata) error {
+ return ErrNotImplemented
+}
+
+//Update_flows_incrementally updates (add/remove) the flows on a given device
+func (oo *OpenOLT) Update_flows_incrementally(device *voltha.Device, flows *openflow_13.FlowChanges, groups *openflow_13.FlowGroupChanges, flowMetadata *voltha.FlowMetadata) error {
+ log.Debugw("Update_flows_incrementally", log.Fields{"deviceId": device.Id, "flows": flows, "flowMetadata": flowMetadata})
+ ctx := context.Background()
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ return handler.UpdateFlowsIncrementally(ctx, device, flows, groups, flowMetadata)
+ }
+ return NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil).Log()
+}
+
+//Update_pm_config returns PmConfigs nil or error
+func (oo *OpenOLT) Update_pm_config(device *voltha.Device, pmConfigs *voltha.PmConfigs) error {
+ return ErrNotImplemented
+}
+
+//Receive_packet_out sends packet out to the device
+func (oo *OpenOLT) Receive_packet_out(deviceID string, egressPortNo int, packet *openflow_13.OfpPacketOut) error {
+ log.Debugw("Receive_packet_out", log.Fields{"deviceId": deviceID, "egress_port_no": egressPortNo, "pkt": packet})
+ ctx := context.Background()
+ if handler := oo.getDeviceHandler(deviceID); handler != nil {
+ return handler.PacketOut(ctx, egressPortNo, packet)
+ }
+ return NewErrNotFound("device-handler", log.Fields{"device-id": deviceID}, nil).Log()
+}
+
+//Suppress_event unimplemented
+func (oo *OpenOLT) Suppress_event(filter *voltha.EventFilter) error {
+ return ErrNotImplemented
+}
+
+//Unsuppress_event unimplemented
+func (oo *OpenOLT) Unsuppress_event(filter *voltha.EventFilter) error {
+ return ErrNotImplemented
+}
+
+//Download_image unimplemented
+func (oo *OpenOLT) Download_image(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, ErrNotImplemented
+}
+
+//Get_image_download_status unimplemented
+func (oo *OpenOLT) Get_image_download_status(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, ErrNotImplemented
+}
+
+//Cancel_image_download unimplemented
+func (oo *OpenOLT) Cancel_image_download(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, ErrNotImplemented
+}
+
+//Activate_image_update unimplemented
+func (oo *OpenOLT) Activate_image_update(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, ErrNotImplemented
+}
+
+//Revert_image_update unimplemented
+func (oo *OpenOLT) Revert_image_update(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, ErrNotImplemented
+}
+
+// Enable_port to Enable PON/NNI interface
+func (oo *OpenOLT) Enable_port(deviceID string, port *voltha.Port) error {
+ log.Infow("Enable_port", log.Fields{"deviceId": deviceID, "port": port})
+ return oo.enableDisablePort(deviceID, port, true)
+}
+
+// Disable_port to Disable pon/nni interface
+func (oo *OpenOLT) Disable_port(deviceID string, port *voltha.Port) error {
+ log.Infow("Disable_port", log.Fields{"deviceId": deviceID, "port": port})
+ return oo.enableDisablePort(deviceID, port, false)
+}
+
+// enableDisablePort to Disable pon or Enable PON interface
+func (oo *OpenOLT) enableDisablePort(deviceID string, port *voltha.Port, enablePort bool) error {
+ log.Infow("enableDisablePort", log.Fields{"deviceId": deviceID, "port": port})
+ if port == nil {
+ return NewErrInvalidValue(log.Fields{
+ "reason": "port cannot be nil",
+ "device-id": deviceID,
+ "port": nil}, nil).Log()
+ }
+ if handler := oo.getDeviceHandler(deviceID); handler != nil {
+ log.Debugw("Enable_Disable_Port", log.Fields{"deviceId": deviceID, "port": port})
+ if enablePort {
+ if err := handler.EnablePort(port); err != nil {
+ log.Errorw("error-occurred-during-enable-port", log.Fields{"deviceID": deviceID, "port": port, "error": err})
+ return err
+ }
+ } else {
+ if err := handler.DisablePort(port); err != nil {
+ log.Errorw("error-occurred-during-disable-port", log.Fields{"Device": deviceID, "port": port})
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+//Child_device_lost deletes the ONU and its references from PONResources
+func (oo *OpenOLT) Child_device_lost(deviceID string, pPortNo uint32, onuID uint32) error {
+ log.Infow("Child-device-lost", log.Fields{"parentId": deviceID})
+ ctx := context.Background()
+ if handler := oo.getDeviceHandler(deviceID); handler != nil {
+ return handler.ChildDeviceLost(ctx, pPortNo, onuID)
+ }
+ return NewErrNotFound("device-handler", log.Fields{"device-id": deviceID}, nil).Log()
+}
diff --git a/internal/pkg/core/openolt_eventmgr.go b/internal/pkg/core/openolt_eventmgr.go
new file mode 100644
index 0000000..4e21d11
--- /dev/null
+++ b/internal/pkg/core/openolt_eventmgr.go
@@ -0,0 +1,504 @@
+/*
+ * 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 core provides APIs for the openOLT adapter
+package core
+
+import (
+ ctx "context"
+ "fmt"
+ "strconv"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "github.com/opencord/voltha-protos/v3/go/common"
+ oop "github.com/opencord/voltha-protos/v3/go/openolt"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+const (
+ onuDiscoveryEvent = "ONU_DISCOVERY"
+ onuLosEvent = "ONU_LOSS_OF_SIGNAL"
+ onuLobEvent = "ONU_LOSS_OF_BURST"
+ onuLopcMissEvent = "ONU_LOPC_MISS"
+ onuLopcMicErrorEvent = "ONU_LOPC_MIC_ERROR"
+ oltLosEvent = "OLT_LOSS_OF_SIGNAL"
+ oltIndicationDown = "OLT_DOWN_INDICATION"
+ onuDyingGaspEvent = "ONU_DYING_GASP"
+ onuSignalsFailEvent = "ONU_SIGNALS_FAIL"
+ onuStartupFailEvent = "ONU_STARTUP_FAIL"
+ onuSignalDegradeEvent = "ONU_SIGNAL_DEGRADE"
+ onuDriftOfWindowEvent = "ONU_DRIFT_OF_WINDOW"
+ onuActivationFailEvent = "ONU_ACTIVATION_FAIL"
+ onuProcessingErrorEvent = "ONU_PROCESSING_ERROR"
+ onuTiwiEvent = "ONU_TRANSMISSION_WARNING"
+ onuLossOmciEvent = "ONU_LOSS_OF_OMCI_CHANNEL"
+ onuLossOfKeySyncEvent = "ONU_LOSS_OF_KEY_SYNC"
+ onuLossOfFrameEvent = "ONU_LOSS_OF_FRAME"
+ onuLossOfPloamEvent = "ONU_LOSS_OF_PLOAM"
+ ponIntfDownIndiction = "OLT_PON_INTERFACE_DOWN"
+ onuDeactivationFailureEvent = "ONU_DEACTIVATION_FAILURE"
+)
+
+const (
+ pon = voltha.EventSubCategory_PON
+ olt = voltha.EventSubCategory_OLT
+ ont = voltha.EventSubCategory_ONT
+ onu = voltha.EventSubCategory_ONU
+ nni = voltha.EventSubCategory_NNI
+ service = voltha.EventCategory_SERVICE
+ security = voltha.EventCategory_SECURITY
+ equipment = voltha.EventCategory_EQUIPMENT
+ processing = voltha.EventCategory_PROCESSING
+ environment = voltha.EventCategory_ENVIRONMENT
+ communication = voltha.EventCategory_COMMUNICATION
+)
+
+const (
+ // statusCheckOn represents status check On
+ statusCheckOn = "on"
+ // statusCheckOff represents status check Off
+ statusCheckOff = "off"
+ // operationStateUp represents operation state Up
+ operationStateUp = "up"
+ // operationStateDown represents operation state Down
+ operationStateDown = "down"
+ // base10 represents base 10 conversion
+ base10 = 10
+)
+
+// OpenOltEventMgr struct contains
+type OpenOltEventMgr struct {
+ eventProxy adapterif.EventProxy
+ handler *DeviceHandler
+}
+
+// NewEventMgr is a Function to get a new event manager struct for the OpenOLT to process and publish OpenOLT event
+func NewEventMgr(eventProxy adapterif.EventProxy, handler *DeviceHandler) *OpenOltEventMgr {
+ var em OpenOltEventMgr
+ em.eventProxy = eventProxy
+ em.handler = handler
+ return &em
+}
+
+// ProcessEvents is function to process and publish OpenOLT event
+func (em *OpenOltEventMgr) ProcessEvents(alarmInd *oop.AlarmIndication, deviceID string, raisedTs int64) error {
+ var err error
+ switch alarmInd.Data.(type) {
+ case *oop.AlarmIndication_LosInd:
+ log.Infow("Received LOS indication", log.Fields{"alarm_ind": alarmInd})
+ err = em.oltLosIndication(alarmInd.GetLosInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuAlarmInd:
+ log.Infow("Received onu alarm indication ", log.Fields{"alarm_ind": alarmInd})
+ err = em.onuAlarmIndication(alarmInd.GetOnuAlarmInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_DyingGaspInd:
+ log.Infow("Received dying gasp indication", log.Fields{"alarm_ind": alarmInd})
+ err = em.onuDyingGaspIndication(alarmInd.GetDyingGaspInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuActivationFailInd:
+ log.Infow("Received onu activation fail indication ", log.Fields{"alarm_ind": alarmInd})
+ err = em.onuActivationFailIndication(alarmInd.GetOnuActivationFailInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuLossOmciInd:
+ log.Infow("Received onu loss omci indication ", log.Fields{"alarm_ind": alarmInd})
+ err = em.onuLossOmciIndication(alarmInd.GetOnuLossOmciInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuDriftOfWindowInd:
+ log.Infow("Received onu drift of window indication ", log.Fields{"alarm_ind": alarmInd})
+ err = em.onuDriftOfWindowIndication(alarmInd.GetOnuDriftOfWindowInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuSignalDegradeInd:
+ log.Infow("Received onu signal degrade indication ", log.Fields{"alarm_ind": alarmInd})
+ err = em.onuSignalDegradeIndication(alarmInd.GetOnuSignalDegradeInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuSignalsFailInd:
+ log.Infow("Received onu signal fail indication ", log.Fields{"alarm_ind": alarmInd})
+ err = em.onuSignalsFailIndication(alarmInd.GetOnuSignalsFailInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuStartupFailInd:
+ log.Infow("Received onu startup fail indication ", log.Fields{"alarm_ind": alarmInd})
+ err = em.onuStartupFailedIndication(alarmInd.GetOnuStartupFailInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuTiwiInd:
+ log.Infow("Received onu transmission warning indication ", log.Fields{"alarm_ind": alarmInd})
+ log.Infow("Not implemented yet", log.Fields{"alarm_ind": "Onu-Transmission-indication"})
+ case *oop.AlarmIndication_OnuLossOfSyncFailInd:
+ log.Infow("Received onu Loss of Sync Fail indication ", log.Fields{"alarm_ind": alarmInd})
+ err = em.onuLossOfSyncIndication(alarmInd.GetOnuLossOfSyncFailInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuItuPonStatsInd:
+ log.Infow("Received onu Itu Pon Stats indication ", log.Fields{"alarm_ind": alarmInd})
+ log.Infow("Not implemented yet", log.Fields{"alarm_ind": alarmInd})
+ case *oop.AlarmIndication_OnuDeactivationFailureInd:
+ log.Infow("Received onu deactivation failure indication ", log.Fields{"alarm_ind": alarmInd})
+ err = em.onuDeactivationFailureIndication(alarmInd.GetOnuDeactivationFailureInd(), deviceID, raisedTs)
+ default:
+ err = NewErrInvalidValue(log.Fields{"indication-type": alarmInd}, nil)
+ }
+ if err != nil {
+ return NewErrCommunication("publish-message", log.Fields{"indication-type": alarmInd}, err).Log()
+ }
+ return nil
+}
+
+// oltUpDownIndication handles Up and Down state of an OLT
+func (em *OpenOltEventMgr) oltUpDownIndication(oltIndication *oop.OltIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["oper-state"] = oltIndication.OperState
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ if oltIndication.OperState == operationStateDown {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", oltIndicationDown, "RAISE_EVENT")
+ } else if oltIndication.OperState == operationStateUp {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", oltIndicationDown, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, communication, olt, raisedTs); err != nil {
+ log.Errorw("Failed to send OLT event", log.Fields{"err": err})
+ return err
+ }
+ log.Infow("OLT UpDown event sent to KAFKA", log.Fields{})
+ return nil
+}
+
+// OnuDiscoveryIndication is an exported method to handle ONU discovery event
+func (em *OpenOltEventMgr) OnuDiscoveryIndication(onuDisc *oop.OnuDiscIndication, deviceID string, OnuID uint32, serialNumber string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["onu-id"] = strconv.FormatUint(uint64(OnuID), base10)
+ context["intf-id"] = strconv.FormatUint(uint64(onuDisc.IntfId), base10)
+ context["serial-number"] = serialNumber
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuDiscoveryEvent, "RAISE_EVENT")
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, equipment, pon, raisedTs); err != nil {
+ log.Errorw("Failed to send ONU discovery event", log.Fields{"serial-number": serialNumber, "intf-id": onuDisc.IntfId})
+ return err
+ }
+ log.Infow("ONU discovery event sent to KAFKA", log.Fields{"serial-number": serialNumber, "intf-id": onuDisc.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) oltLosIndication(oltLos *oop.LosIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["intf-id"] = strconv.FormatUint(uint64(oltLos.IntfId), base10)
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ if oltLos.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", oltLosEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", oltLosEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, communication, olt, raisedTs); err != nil {
+ log.Errorw("Failed to send OLT loss of signal event", log.Fields{"intf-id": oltLos.IntfId})
+ return err
+ }
+ log.Infow("OLT LOS event sent to KAFKA", log.Fields{"intf-id": oltLos.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuDyingGaspIndication(dgi *oop.DyingGaspIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ var serialNumber string
+ context := make(map[string]string)
+ /* Populating event context */
+ serialNumber = ""
+ onu := em.handler.formOnuKey(dgi.IntfId, dgi.OnuId)
+ if onu, ok := em.handler.onus.Load(onu); ok {
+ serialNumber = onu.(*OnuDevice).serialNumber
+ }
+ context["serial-number"] = serialNumber
+ context["intf-id"] = strconv.FormatUint(uint64(dgi.IntfId), base10)
+ context["onu-id"] = strconv.FormatUint(uint64(dgi.OnuId), base10)
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuDyingGaspEvent, "EVENT")
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, communication, pon, raisedTs); err != nil {
+ log.Errorw("Failed to send ONU Dying gasp event", log.Fields{"intf-id": dgi.IntfId, "onu-id": dgi.OnuId})
+ return err
+ }
+ log.Infow("ONU dying gasp event sent to KAFKA", log.Fields{"intf-id": dgi.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuAlarmIndication(onuAlarm *oop.OnuAlarmIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["intf-id"] = strconv.FormatUint(uint64(onuAlarm.IntfId), base10)
+ context["onu-id"] = strconv.FormatUint(uint64(onuAlarm.OnuId), base10)
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ if onuAlarm.LosStatus == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLosEvent, "RAISE_EVENT")
+ } else if onuAlarm.LosStatus == statusCheckOff {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLosEvent, "CLEAR_EVENT")
+ } else if onuAlarm.LobStatus == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLobEvent, "RAISE_EVENT")
+ } else if onuAlarm.LobStatus == statusCheckOff {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLobEvent, "CLEAR_EVENT")
+ } else if onuAlarm.LopcMissStatus == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLopcMissEvent, "RAISE_EVENT")
+ } else if onuAlarm.LopcMissStatus == statusCheckOff {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLopcMissEvent, "CLEAR_EVENT")
+ } else if onuAlarm.LopcMicErrorStatus == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLopcMicErrorEvent, "RAISE_EVENT")
+ } else if onuAlarm.LopcMicErrorStatus == statusCheckOff {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLopcMicErrorEvent, "CLEAR_EVENT")
+ } else if onuAlarm.LofiStatus == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOfFrameEvent, "RAISE_EVENT")
+ } else if onuAlarm.LofiStatus == statusCheckOff {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOfFrameEvent, "CLEAR_EVENT")
+ } else if onuAlarm.LoamiStatus == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOfPloamEvent, "RAISE_EVENT")
+ } else if onuAlarm.LoamiStatus == statusCheckOff {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOfPloamEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, communication, onu, raisedTs); err != nil {
+ log.Errorw("Failed to send ONU Los event", log.Fields{"onu-id": onuAlarm.OnuId, "intf-id": onuAlarm.IntfId})
+ return err
+ }
+ log.Infow("ONU LOS event sent to KAFKA", log.Fields{"onu-id": onuAlarm.OnuId, "intf-id": onuAlarm.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuActivationFailIndication(oaf *oop.OnuActivationFailureIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["intf-id"] = strconv.FormatUint(uint64(oaf.IntfId), base10)
+ context["onu-id"] = strconv.FormatUint(uint64(oaf.OnuId), base10)
+ context["fail-reason"] = strconv.FormatUint(uint64(oaf.FailReason), base10)
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuActivationFailEvent, "RAISE_EVENT")
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, equipment, pon, raisedTs); err != nil {
+ log.Errorw("Failed to send ONU activation failure event", log.Fields{"onu-id": oaf.OnuId, "intf-id": oaf.IntfId})
+ return err
+ }
+ log.Infow("ONU activation failure event sent to KAFKA", log.Fields{"onu-id": oaf.OnuId, "intf-id": oaf.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuLossOmciIndication(onuLossOmci *oop.OnuLossOfOmciChannelIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["intf-id"] = strconv.FormatUint(uint64(onuLossOmci.IntfId), base10)
+ context["onu-id"] = strconv.FormatUint(uint64(onuLossOmci.OnuId), base10)
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ if onuLossOmci.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOmciEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOmciEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, communication, pon, raisedTs); err != nil {
+ log.Errorw("Failed to send ONU loss of OMCI channel event", log.Fields{"onu-id": onuLossOmci.OnuId, "intf-id": onuLossOmci.IntfId})
+ return err
+ }
+ log.Infow("ONU loss of OMCI channel event sent to KAFKA", log.Fields{"onu-id": onuLossOmci.OnuId, "intf-id": onuLossOmci.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuDriftOfWindowIndication(onuDriftWindow *oop.OnuDriftOfWindowIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["intf-id"] = strconv.FormatUint(uint64(onuDriftWindow.IntfId), base10)
+ context["onu-id"] = strconv.FormatUint(uint64(onuDriftWindow.OnuId), base10)
+ context["drift"] = strconv.FormatUint(uint64(onuDriftWindow.Drift), base10)
+ context["new-eqd"] = strconv.FormatUint(uint64(onuDriftWindow.NewEqd), base10)
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ if onuDriftWindow.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuDriftOfWindowEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuDriftOfWindowEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, communication, pon, raisedTs); err != nil {
+ log.Errorw("Failed to send ONU drift of window event", log.Fields{"onu-id": onuDriftWindow.OnuId, "intf-id": onuDriftWindow.IntfId})
+ return err
+ }
+ log.Infow("ONU drift of window event sent to KAFKA", log.Fields{"onu-id": onuDriftWindow.OnuId, "intf-id": onuDriftWindow.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuSignalDegradeIndication(onuSignalDegrade *oop.OnuSignalDegradeIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["intf-id"] = strconv.FormatUint(uint64(onuSignalDegrade.IntfId), base10)
+ context["onu-id"] = strconv.FormatUint(uint64(onuSignalDegrade.OnuId), base10)
+ context["inverse-bit-error-rate"] = strconv.FormatUint(uint64(onuSignalDegrade.InverseBitErrorRate), base10)
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ if onuSignalDegrade.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuSignalDegradeEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuSignalDegradeEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, communication, pon, raisedTs); err != nil {
+ log.Errorw("Failed to send ONU signals degrade event", log.Fields{"onu-id": onuSignalDegrade.OnuId, "intf-id": onuSignalDegrade.IntfId})
+ return err
+ }
+ log.Infow("ONU signal degrade event sent to KAFKA", log.Fields{"onu-id": onuSignalDegrade.OnuId, "intf-id": onuSignalDegrade.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuSignalsFailIndication(onuSignalsFail *oop.OnuSignalsFailureIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["onu-id"] = strconv.FormatUint(uint64(onuSignalsFail.OnuId), base10)
+ context["intf-id"] = strconv.FormatUint(uint64(onuSignalsFail.IntfId), base10)
+ context["inverse-bit-error-rate"] = strconv.FormatUint(uint64(onuSignalsFail.InverseBitErrorRate), base10)
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ if onuSignalsFail.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuSignalsFailEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuSignalsFailEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, communication, pon, raisedTs); err != nil {
+ log.Errorw("Failed to send ONU signals fail event", log.Fields{"onu-id": onuSignalsFail.OnuId, "intf-id": onuSignalsFail.IntfId})
+ return err
+ }
+ log.Infow("ONU signals fail event sent to KAFKA", log.Fields{"onu-id": onuSignalsFail.OnuId, "intf-id": onuSignalsFail.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuStartupFailedIndication(onuStartupFail *oop.OnuStartupFailureIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["onu-id"] = strconv.FormatUint(uint64(onuStartupFail.OnuId), base10)
+ context["intf-id"] = strconv.FormatUint(uint64(onuStartupFail.IntfId), base10)
+
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ if onuStartupFail.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuStartupFailEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuStartupFailEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, communication, pon, raisedTs); err != nil {
+ log.Errorw("Failed to send ONU startup fail event", log.Fields{"onu-id": onuStartupFail.OnuId, "intf-id": onuStartupFail.IntfId})
+ return err
+ }
+ log.Infow("ONU startup fail event sent to KAFKA", log.Fields{"onu-id": onuStartupFail.OnuId, "intf-id": onuStartupFail.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuLossOfSyncIndication(onuLOKI *oop.OnuLossOfKeySyncFailureIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["onu-id"] = strconv.FormatUint(uint64(onuLOKI.OnuId), base10)
+ context["intf-id"] = strconv.FormatUint(uint64(onuLOKI.IntfId), base10)
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ if onuLOKI.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOfKeySyncEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOfKeySyncEvent, "CLEAR_EVENT")
+ }
+
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, security, onu, raisedTs); err != nil {
+ log.Errorw("Failed to send ONU loss of key sync event", log.Fields{"onu-id": onuLOKI.OnuId, "intf-id": onuLOKI.IntfId})
+ return err
+ }
+ log.Infow("ONU loss of key sync event sent to KAFKA", log.Fields{"onu-id": onuLOKI.OnuId, "intf-id": onuLOKI.IntfId})
+ return nil
+}
+
+// oltIntfOperIndication handles Up and Down state of an OLT PON ports
+func (em *OpenOltEventMgr) oltIntfOperIndication(ifindication *oop.IntfOperIndication, deviceID string, raisedTs int64) {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ portID := IntfIDToPortNo(ifindication.IntfId, voltha.Port_PON_OLT)
+ device, err := em.handler.coreProxy.GetDevice(ctx.Background(), deviceID, deviceID)
+ if err != nil {
+ log.Errorw("Error while fetching Device object", log.Fields{"DeviceId": deviceID})
+ }
+ for _, port := range device.Ports {
+ if port.PortNo == portID {
+ // Events are suppressed if the Port Adminstate is not enabled.
+ if port.AdminState != common.AdminState_ENABLED {
+ log.Infow("Port disable/enable event not generated because, The port is not enabled by operator", log.Fields{"deviceId": deviceID, "port": port})
+ return
+ }
+ break
+ }
+ }
+ /* Populating event context */
+ context["oper-state"] = ifindication.GetOperState()
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+
+ if ifindication.GetOperState() == operationStateDown {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", ponIntfDownIndiction, "RAISE_EVENT")
+ } else if ifindication.OperState == operationStateUp {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", ponIntfDownIndiction, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, communication, olt, raisedTs); err != nil {
+ log.Errorw("failed-to-send-olt-intf-oper-status-event", log.Fields{"err": err})
+ }
+ log.Info("sent-olt-intf-oper-status-event-to-kafka")
+}
+
+func (em *OpenOltEventMgr) onuDeactivationFailureIndication(onuDFI *oop.OnuDeactivationFailureIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context["onu-id"] = strconv.FormatUint(uint64(onuDFI.OnuId), base10)
+ context["intf-id"] = strconv.FormatUint(uint64(onuDFI.IntfId), base10)
+ context["failure-reason"] = strconv.FormatUint(uint64(onuDFI.FailReason), base10)
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ de.DeviceEventName = onuDeactivationFailureEvent
+
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(&de, equipment, onu, raisedTs); err != nil {
+ log.Errorw("Failed to send ONU deactivation failure event", log.Fields{"onu-id": onuDFI.OnuId, "intf-id": onuDFI.IntfId})
+ return err
+ }
+ log.Infow("ONU deactivation failure event sent to KAFKA", log.Fields{"onu-id": onuDFI.OnuId, "intf-id": onuDFI.IntfId})
+ return nil
+}
diff --git a/internal/pkg/core/openolt_eventmgr_test.go b/internal/pkg/core/openolt_eventmgr_test.go
new file mode 100644
index 0000000..d5f1520
--- /dev/null
+++ b/internal/pkg/core/openolt_eventmgr_test.go
@@ -0,0 +1,149 @@
+/*
+ * 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 core provides APIs for the openOLT adapter
+package core
+
+import (
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/opencord/voltha-openolt-adapter/pkg/mocks"
+ oop "github.com/opencord/voltha-protos/v3/go/openolt"
+)
+
+func mockEventMgr() *OpenOltEventMgr {
+ ep := &mocks.MockEventProxy{}
+ dh := &DeviceHandler{}
+ dh.onus = sync.Map{}
+ dh.onus.Store(dh.formOnuKey(1, 1), &OnuDevice{deviceID: "TEST_ONU",
+ deviceType: "ONU",
+ serialNumber: "TEST_ONU_123",
+ onuID: 1, intfID: 1})
+ return NewEventMgr(ep, dh)
+}
+func TestOpenOltEventMgr_ProcessEvents(t *testing.T) {
+ em := mockEventMgr()
+ type args struct {
+ alarmInd *oop.AlarmIndication
+ deviceID string
+ raisedTs int64
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ // TODO: Add test cases.
+ // LosIndication alarms
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_LosInd{LosInd: &oop.LosIndication{IntfId: 1, Status: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_LosInd{LosInd: &oop.LosIndication{IntfId: 1}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_LosInd{LosInd: &oop.LosIndication{IntfId: 1, Status: "on"}}}}},
+
+ // OnuAlarmIndication alams
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LosStatus: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LosStatus: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LobStatus: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LobStatus: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LopcMissStatus: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LopcMissStatus: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LopcMicErrorStatus: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LopcMicErrorStatus: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LofiStatus: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LofiStatus: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LoamiStatus: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LoamiStatus: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &oop.OnuAlarmIndication{IntfId: 1, OnuId: 3, LosStatus: "on"}}}}},
+
+ // AlarmIndication_DyingGaspInd
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_DyingGaspInd{DyingGaspInd: &oop.DyingGaspIndication{IntfId: 1, OnuId: 1, Status: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_DyingGaspInd{DyingGaspInd: &oop.DyingGaspIndication{IntfId: 1, OnuId: 1, Status: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_DyingGaspInd{DyingGaspInd: &oop.DyingGaspIndication{IntfId: 1, OnuId: 1}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_DyingGaspInd{DyingGaspInd: &oop.DyingGaspIndication{IntfId: 1, OnuId: 1}}}}},
+
+ // AlarmIndication_OnuActivationFailInd
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuActivationFailInd{OnuActivationFailInd: &oop.OnuActivationFailureIndication{IntfId: 1, OnuId: 3}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuActivationFailInd{OnuActivationFailInd: &oop.OnuActivationFailureIndication{IntfId: 1, OnuId: 3}}}}},
+
+ // AlarmIndication_OnuLossOmciInd
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuLossOmciInd{OnuLossOmciInd: &oop.OnuLossOfOmciChannelIndication{IntfId: 1, OnuId: 3, Status: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuLossOmciInd{OnuLossOmciInd: &oop.OnuLossOfOmciChannelIndication{IntfId: 1, OnuId: 3, Status: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuLossOmciInd{OnuLossOmciInd: &oop.OnuLossOfOmciChannelIndication{IntfId: 1, OnuId: 3, Status: "on"}}}}},
+
+ // AlarmIndication_OnuDriftOfWindowInd
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuDriftOfWindowInd{OnuDriftOfWindowInd: &oop.OnuDriftOfWindowIndication{IntfId: 1, OnuId: 3, Status: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuDriftOfWindowInd{OnuDriftOfWindowInd: &oop.OnuDriftOfWindowIndication{IntfId: 1, OnuId: 3, Status: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuDriftOfWindowInd{OnuDriftOfWindowInd: &oop.OnuDriftOfWindowIndication{IntfId: 1, OnuId: 3, Drift: 10, NewEqd: 10}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuDriftOfWindowInd{OnuDriftOfWindowInd: &oop.OnuDriftOfWindowIndication{IntfId: 1, OnuId: 3, Status: "on"}}}}},
+
+ // AlarmIndication_OnuSignalDegradeInd
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuSignalDegradeInd{OnuSignalDegradeInd: &oop.OnuSignalDegradeIndication{IntfId: 1, OnuId: 3, Status: "on", InverseBitErrorRate: 100}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuSignalDegradeInd{OnuSignalDegradeInd: &oop.OnuSignalDegradeIndication{IntfId: 1, OnuId: 3, Status: "off", InverseBitErrorRate: 100}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuSignalDegradeInd{OnuSignalDegradeInd: &oop.OnuSignalDegradeIndication{IntfId: 1, OnuId: 3, InverseBitErrorRate: 100}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuSignalDegradeInd{OnuSignalDegradeInd: &oop.OnuSignalDegradeIndication{IntfId: 1, OnuId: 3, InverseBitErrorRate: 100}}}}},
+
+ // AlarmIndication_OnuSignalsFailInd
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuSignalsFailInd{OnuSignalsFailInd: &oop.OnuSignalsFailureIndication{IntfId: 1, OnuId: 3, Status: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuSignalsFailInd{OnuSignalsFailInd: &oop.OnuSignalsFailureIndication{IntfId: 1, OnuId: 3, Status: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuSignalsFailInd{OnuSignalsFailInd: &oop.OnuSignalsFailureIndication{IntfId: 1, OnuId: 3, InverseBitErrorRate: 100}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuSignalsFailInd{OnuSignalsFailInd: &oop.OnuSignalsFailureIndication{IntfId: 1, OnuId: 3, InverseBitErrorRate: 100}}}}},
+
+ // AlarmIndication_OnuProcessingErrorInd
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuProcessingErrorInd{OnuProcessingErrorInd: &oop.OnuProcessingErrorIndication{IntfId: 1, OnuId: 3}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuProcessingErrorInd{OnuProcessingErrorInd: &oop.OnuProcessingErrorIndication{IntfId: 1, OnuId: 3}}}}},
+
+ // AlarmIndication_OnuTiwiInd
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuTiwiInd{OnuTiwiInd: &oop.OnuTransmissionInterferenceWarning{IntfId: 1, OnuId: 3, Status: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuTiwiInd{OnuTiwiInd: &oop.OnuTransmissionInterferenceWarning{IntfId: 1, OnuId: 3, Status: "on", Drift: 100}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuTiwiInd{OnuTiwiInd: &oop.OnuTransmissionInterferenceWarning{IntfId: 1, OnuId: 3}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuTiwiInd{OnuTiwiInd: &oop.OnuTransmissionInterferenceWarning{IntfId: 1, OnuId: 3}}}}},
+
+ // AlarmIndication_onuLossOfKeySyncInd
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuLossOfSyncFailInd{OnuLossOfSyncFailInd: &oop.OnuLossOfKeySyncFailureIndication{IntfId: 1, OnuId: 3, Status: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuLossOfSyncFailInd{OnuLossOfSyncFailInd: &oop.OnuLossOfKeySyncFailureIndication{IntfId: 1, OnuId: 3, Status: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ em.ProcessEvents(tt.args.alarmInd, tt.args.deviceID, tt.args.raisedTs)
+ })
+ }
+}
+
+func TestOpenOltEventMgr_OnuDiscoveryIndication(t *testing.T) {
+ em := mockEventMgr()
+ type args struct {
+ onuDisc *oop.OnuDiscIndication
+ deviceID string
+ OnuID uint32
+ serialNumber string
+ raisedTs int64
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ // TODO: Add test cases.
+ {"OnuDiscoveryIndication", args{onuDisc: &oop.OnuDiscIndication{IntfId: 1, SerialNumber: &oop.SerialNumber{VendorId: []byte("TWSH"), VendorSpecific: []byte("1234")}}, deviceID: "olt", OnuID: 3, serialNumber: "1234", raisedTs: time.Now().Unix()}},
+ {"OnuDiscoveryIndication", args{onuDisc: &oop.OnuDiscIndication{}, raisedTs: time.Now().Unix()}},
+ {"OnuDiscoveryIndication", args{onuDisc: &oop.OnuDiscIndication{}}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ em.OnuDiscoveryIndication(tt.args.onuDisc, tt.args.deviceID, tt.args.OnuID, tt.args.serialNumber, tt.args.raisedTs)
+ })
+ }
+}
diff --git a/internal/pkg/core/openolt_flowmgr.go b/internal/pkg/core/openolt_flowmgr.go
new file mode 100644
index 0000000..fe03a79
--- /dev/null
+++ b/internal/pkg/core/openolt_flowmgr.go
@@ -0,0 +1,3044 @@
+/*
+ * 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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "context"
+ "crypto/md5"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "math/big"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ tp "github.com/opencord/voltha-lib-go/v3/pkg/techprofile"
+ rsrcMgr "github.com/opencord/voltha-openolt-adapter/internal/pkg/resourcemanager"
+ "github.com/opencord/voltha-protos/v3/go/common"
+ ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+ ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ openoltpb2 "github.com/opencord/voltha-protos/v3/go/openolt"
+ tp_pb "github.com/opencord/voltha-protos/v3/go/tech_profile"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+
+ //deepcopy "github.com/getlantern/deepcopy"
+ "github.com/EagleChen/mapmutex"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+const (
+ // Flow categories
+
+ //HsiaFlow flow category
+ HsiaFlow = "HSIA_FLOW"
+
+ //EapolFlow flow category
+ EapolFlow = "EAPOL_FLOW"
+
+ //DhcpFlow flow category
+ DhcpFlow = "DHCP_FLOW"
+
+ //MulticastFlow flow category
+ MulticastFlow = "MULTICAST_FLOW"
+
+ //IgmpFlow flow category
+ IgmpFlow = "IGMP_FLOW"
+
+ //IPProtoDhcp flow category
+ IPProtoDhcp = 17
+
+ //IPProtoIgmp flow category
+ IPProtoIgmp = 2
+
+ //EapEthType eapethtype value
+ EapEthType = 0x888e
+ //LldpEthType lldp ethtype value
+ LldpEthType = 0x88cc
+ //IPv4EthType IPv4 ethernet type value
+ IPv4EthType = 0x800
+
+ //IgmpProto proto value
+ IgmpProto = 2
+
+ //FIXME - see also BRDCM_DEFAULT_VLAN in broadcom_onu.py
+
+ //ReservedVlan Transparent Vlan
+ ReservedVlan = 4095
+
+ //DefaultMgmtVlan default vlan value
+ DefaultMgmtVlan = 4091
+
+ // Openolt Flow
+
+ //Upstream constant
+ Upstream = "upstream"
+ //Downstream constant
+ Downstream = "downstream"
+ //Multicast constant
+ Multicast = "multicast"
+ //PacketTagType constant
+ PacketTagType = "pkt_tag_type"
+ //Untagged constant
+ Untagged = "untagged"
+ //SingleTag constant
+ SingleTag = "single_tag"
+ //DoubleTag constant
+ DoubleTag = "double_tag"
+
+ // classifierInfo
+
+ //EthType constant
+ EthType = "eth_type"
+ //EthDst constant
+ EthDst = "eth_dst"
+ //TPID constant
+ TPID = "tpid"
+ //IPProto constant
+ IPProto = "ip_proto"
+ //InPort constant
+ InPort = "in_port"
+ //VlanVid constant
+ VlanVid = "vlan_vid"
+ //VlanPcp constant
+ VlanPcp = "vlan_pcp"
+
+ //UDPDst constant
+ UDPDst = "udp_dst"
+ //UDPSrc constant
+ UDPSrc = "udp_src"
+ //Ipv4Dst constant
+ Ipv4Dst = "ipv4_dst"
+ //Ipv4Src constant
+ Ipv4Src = "ipv4_src"
+ //Metadata constant
+ Metadata = "metadata"
+ //TunnelID constant
+ TunnelID = "tunnel_id"
+ //Output constant
+ Output = "output"
+ //GroupID constant
+ GroupID = "group_id"
+ // Actions
+
+ //PopVlan constant
+ PopVlan = "pop_vlan"
+ //PushVlan constant
+ PushVlan = "push_vlan"
+ //TrapToHost constant
+ TrapToHost = "trap_to_host"
+ //MaxMeterBand constant
+ MaxMeterBand = 2
+ //VlanPCPMask contant
+ VlanPCPMask = 0xFF
+ //VlanvIDMask constant
+ VlanvIDMask = 0xFFF
+ //IntfID constant
+ IntfID = "intfId"
+ //OnuID constant
+ OnuID = "onuId"
+ //UniID constant
+ UniID = "uniId"
+ //PortNo constant
+ PortNo = "portNo"
+ //AllocID constant
+ AllocID = "allocId"
+
+ //NoneOnuID constant
+ NoneOnuID = -1
+ //NoneUniID constant
+ NoneUniID = -1
+ //NoneGemPortID constant
+ NoneGemPortID = -1
+
+ // BinaryStringPrefix is binary string prefix
+ BinaryStringPrefix = "0b"
+ // BinaryBit1 is binary bit 1 expressed as a character
+ BinaryBit1 = '1'
+)
+
+type gemPortKey struct {
+ intfID uint32
+ gemPort uint32
+}
+
+type pendingFlowDeleteKey struct {
+ intfID uint32
+ onuID uint32
+ uniID uint32
+}
+
+type tpLockKey struct {
+ intfID uint32
+ onuID uint32
+ uniID uint32
+}
+
+type schedQueue struct {
+ direction tp_pb.Direction
+ intfID uint32
+ onuID uint32
+ uniID uint32
+ tpID uint32
+ uniPort uint32
+ tpInst *tp.TechProfile
+ meterID uint32
+ flowMetadata *voltha.FlowMetadata
+}
+
+type queueInfoBrief struct {
+ gemPortID uint32
+ servicePriority uint32
+}
+
+//OpenOltFlowMgr creates the Structure of OpenOltFlowMgr obj
+type OpenOltFlowMgr struct {
+ techprofile map[uint32]tp.TechProfileIf
+ deviceHandler *DeviceHandler
+ resourceMgr *rsrcMgr.OpenOltResourceMgr
+ onuIdsLock sync.RWMutex
+ flowsUsedByGemPort map[gemPortKey][]uint32 //gem port id to flow ids
+ packetInGemPort map[rsrcMgr.PacketInInfoKey]uint32 //packet in gem port local cache
+ onuGemInfo map[uint32][]rsrcMgr.OnuGemInfo //onu, gem and uni info local cache
+ lockCache sync.RWMutex
+ pendingFlowDelete sync.Map
+ // The mapmutex.Mutex can be fine tuned to use mapmutex.NewCustomizedMapMutex
+ perUserFlowHandleLock *mapmutex.Mutex
+ interfaceToMcastQueueMap map[uint32]*queueInfoBrief /*pon interface -> multicast queue map. Required to assign GEM to a bucket during group population*/
+}
+
+//NewFlowManager creates OpenOltFlowMgr object and initializes the parameters
+func NewFlowManager(ctx context.Context, dh *DeviceHandler, rMgr *rsrcMgr.OpenOltResourceMgr) *OpenOltFlowMgr {
+ log.Info("Initializing flow manager")
+ var flowMgr OpenOltFlowMgr
+ var err error
+ var idx uint32
+
+ flowMgr.deviceHandler = dh
+ flowMgr.resourceMgr = rMgr
+ flowMgr.techprofile = make(map[uint32]tp.TechProfileIf)
+ if err = flowMgr.populateTechProfilePerPonPort(); err != nil {
+ log.Error("Error while populating tech profile mgr\n")
+ return nil
+ }
+ flowMgr.onuIdsLock = sync.RWMutex{}
+ flowMgr.flowsUsedByGemPort = make(map[gemPortKey][]uint32)
+ flowMgr.packetInGemPort = make(map[rsrcMgr.PacketInInfoKey]uint32)
+ flowMgr.onuGemInfo = make(map[uint32][]rsrcMgr.OnuGemInfo)
+ ponPorts := rMgr.DevInfo.GetPonPorts()
+ //Load the onugem info cache from kv store on flowmanager start
+ for idx = 0; idx < ponPorts; idx++ {
+ if flowMgr.onuGemInfo[idx], err = rMgr.GetOnuGemInfo(ctx, idx); err != nil {
+ log.Error("Failed to load onu gem info cache")
+ }
+ //Load flowID list per gem map per interface from the kvstore.
+ flowMgr.loadFlowIDlistForGem(ctx, idx)
+ }
+ flowMgr.lockCache = sync.RWMutex{}
+ flowMgr.pendingFlowDelete = sync.Map{}
+ flowMgr.perUserFlowHandleLock = mapmutex.NewMapMutex()
+ flowMgr.interfaceToMcastQueueMap = make(map[uint32]*queueInfoBrief)
+ //load interface to multicast queue map from kv store
+ flowMgr.loadInterfaceToMulticastQueueMap(ctx)
+ log.Info("Initialization of flow manager success!!")
+ return &flowMgr
+}
+
+func (f *OpenOltFlowMgr) generateStoredFlowID(flowID uint32, direction string) (uint64, error) {
+ if direction == Upstream {
+ log.Debug("upstream flow, shifting id")
+ return 0x1<<15 | uint64(flowID), nil
+ } else if direction == Downstream {
+ log.Debug("downstream flow, not shifting id")
+ return uint64(flowID), nil
+ } else if direction == Multicast {
+ log.Debug("multicast flow, shifting id")
+ return 0x2<<15 | uint64(flowID), nil
+ } else {
+ return 0, NewErrInvalidValue(log.Fields{"direction": direction}, nil).Log()
+ }
+}
+
+func (f *OpenOltFlowMgr) registerFlow(ctx context.Context, flowFromCore *ofp.OfpFlowStats, deviceFlow *openoltpb2.Flow) {
+ log.Debug("Registering Flow for Device ", log.Fields{"flow": flowFromCore},
+ log.Fields{"device": f.deviceHandler.deviceID})
+ gemPK := gemPortKey{uint32(deviceFlow.AccessIntfId), uint32(deviceFlow.GemportId)}
+ flowIDList, ok := f.flowsUsedByGemPort[gemPK]
+ if !ok {
+ flowIDList = []uint32{deviceFlow.FlowId}
+ }
+ flowIDList = appendUnique(flowIDList, deviceFlow.FlowId)
+ f.flowsUsedByGemPort[gemPK] = flowIDList
+ // update the flowids for a gem to the KVstore
+ f.resourceMgr.UpdateFlowIDsForGem(ctx, uint32(deviceFlow.AccessIntfId), uint32(deviceFlow.GemportId), flowIDList)
+}
+
+func (f *OpenOltFlowMgr) divideAndAddFlow(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, portNo uint32,
+ classifierInfo map[string]interface{}, actionInfo map[string]interface{}, flow *ofp.OfpFlowStats, TpID uint32,
+ UsMeterID uint32, DsMeterID uint32, flowMetadata *voltha.FlowMetadata) {
+ var allocID uint32
+ var gemPorts []uint32
+ var TpInst *tp.TechProfile
+
+ log.Infow("Dividing flow", log.Fields{"intfId": intfID, "onuId": onuID, "uniId": uniID, "portNo": portNo,
+ "classifier": classifierInfo, "action": actionInfo, "UsMeterID": UsMeterID, "DsMeterID": DsMeterID, "TpID": TpID})
+ // only create tcont/gemports if there is actually an onu id. otherwise BAL throws an error. Usually this
+ // is because the flow is an NNI flow and there would be no onu resources associated with it
+ // TODO: properly deal with NNI flows
+ if onuID <= 0 {
+ log.Errorw("No onu id for flow", log.Fields{"portNo": portNo, "classifer": classifierInfo, "action": actionInfo})
+ return
+ }
+
+ uni := getUniPortPath(intfID, int32(onuID), int32(uniID))
+ log.Debugw("Uni port name", log.Fields{"uni": uni})
+
+ tpLockMapKey := tpLockKey{intfID, onuID, uniID}
+ if f.perUserFlowHandleLock.TryLock(tpLockMapKey) {
+ allocID, gemPorts, TpInst = f.createTcontGemports(ctx, intfID, onuID, uniID, uni, portNo, TpID, UsMeterID, DsMeterID, flowMetadata)
+ if allocID == 0 || gemPorts == nil || TpInst == nil {
+ log.Error("alloc-id-gem-ports-tp-unavailable")
+ f.perUserFlowHandleLock.Unlock(tpLockMapKey)
+ return
+ }
+ args := make(map[string]uint32)
+ args[IntfID] = intfID
+ args[OnuID] = onuID
+ args[UniID] = uniID
+ args[PortNo] = portNo
+ args[AllocID] = allocID
+
+ /* Flows can be added specific to gemport if p-bits are received.
+ * If no pbit mentioned then adding flows for all gemports
+ */
+ f.checkAndAddFlow(ctx, args, classifierInfo, actionInfo, flow, TpInst, gemPorts, TpID, uni)
+ f.perUserFlowHandleLock.Unlock(tpLockMapKey)
+ } else {
+ log.Errorw("failed to acquire per user flow handle lock",
+ log.Fields{"intfId": intfID, "onuId": onuID, "uniId": uniID})
+ return
+ }
+}
+
+// CreateSchedulerQueues creates traffic schedulers on the device with the given scheduler configuration and traffic shaping info
+func (f *OpenOltFlowMgr) CreateSchedulerQueues(ctx context.Context, sq schedQueue) error {
+
+ log.Debugw("CreateSchedulerQueues", log.Fields{"Dir": sq.direction, "IntfID": sq.intfID,
+ "OnuID": sq.onuID, "UniID": sq.uniID, "TpID": sq.tpID, "MeterID": sq.meterID,
+ "TpInst": sq.tpInst, "flowMetadata": sq.flowMetadata})
+
+ Direction, err := verifyMeterIDAndGetDirection(sq.meterID, sq.direction)
+ if err != nil {
+ return err
+ }
+
+ /* Lets make a simple assumption that if the meter-id is present on the KV store,
+ * then the scheduler and queues configuration is applied on the OLT device
+ * in the given direction.
+ */
+
+ var SchedCfg *tp_pb.SchedulerConfig
+ KvStoreMeter, err := f.resourceMgr.GetMeterIDForOnu(ctx, Direction, sq.intfID, sq.onuID, sq.uniID, sq.tpID)
+ if err != nil {
+ log.Error("Failed to get meter for intf %d, onuid %d, uniid %d", sq.intfID, sq.onuID, sq.uniID)
+ return err
+ }
+ if KvStoreMeter != nil {
+ if KvStoreMeter.MeterId == sq.meterID {
+ log.Debug("Scheduler already created for upstream")
+ return nil
+ }
+ return NewErrInvalidValue(log.Fields{
+ "unsupported": "meter-id",
+ "kv-store-meter-id": KvStoreMeter.MeterId,
+ "meter-id-in-flow": sq.meterID}, nil).Log()
+ }
+
+ log.Debugw("Meter-does-not-exist-Creating-new", log.Fields{"MeterID": sq.meterID, "Direction": Direction})
+
+ if sq.direction == tp_pb.Direction_UPSTREAM {
+ SchedCfg, err = f.techprofile[sq.intfID].GetUsScheduler(sq.tpInst)
+ } else if sq.direction == tp_pb.Direction_DOWNSTREAM {
+ SchedCfg, err = f.techprofile[sq.intfID].GetDsScheduler(sq.tpInst)
+ }
+
+ if err != nil {
+ log.Errorw("Unable to get Scheduler config", log.Fields{"IntfID": sq.intfID, "Direction": sq.direction, "Error": err})
+ return err
+ }
+
+ var meterConfig *ofp.OfpMeterConfig
+ if sq.flowMetadata != nil {
+ for _, meter := range sq.flowMetadata.Meters {
+ if sq.meterID == meter.MeterId {
+ meterConfig = meter
+ log.Debugw("Found-meter-config-from-flowmetadata", log.Fields{"meterConfig": meterConfig})
+ break
+ }
+ }
+ } else {
+ log.Error("Flow-metadata-is-not-present-in-flow")
+ }
+ if meterConfig == nil {
+ return NewErrNotFound("meterbands", log.Fields{
+ "reason": "Could-not-get-meterbands-from-flowMetadata",
+ "flow-metadata": sq.flowMetadata,
+ "meter-id": sq.meterID}, nil).Log()
+ } else if len(meterConfig.Bands) < MaxMeterBand {
+ log.Errorw("Invalid-number-of-bands-in-meter", log.Fields{"Bands": meterConfig.Bands, "MeterID": sq.meterID})
+ return NewErrInvalidValue(log.Fields{
+ "reason": "Invalid-number-of-bands-in-meter",
+ "meterband-count": len(meterConfig.Bands),
+ "metabands": meterConfig.Bands,
+ "meter-id": sq.meterID}, nil).Log()
+ }
+ cir := meterConfig.Bands[0].Rate
+ cbs := meterConfig.Bands[0].BurstSize
+ eir := meterConfig.Bands[1].Rate
+ ebs := meterConfig.Bands[1].BurstSize
+ pir := cir + eir
+ pbs := cbs + ebs
+ TrafficShaping := &tp_pb.TrafficShapingInfo{Cir: cir, Cbs: cbs, Pir: pir, Pbs: pbs}
+
+ TrafficSched := []*tp_pb.TrafficScheduler{f.techprofile[sq.intfID].GetTrafficScheduler(sq.tpInst, SchedCfg, TrafficShaping)}
+
+ if err := f.pushSchedulerQueuesToDevice(ctx, sq, TrafficShaping, TrafficSched); err != nil {
+ log.Errorw("Failed to push traffic scheduler and queues to device", log.Fields{"intfID": sq.intfID, "direction": sq.direction})
+ return err
+ }
+
+ /* After we successfully applied the scheduler configuration on the OLT device,
+ * store the meter id on the KV store, for further reference.
+ */
+ if err := f.resourceMgr.UpdateMeterIDForOnu(ctx, Direction, sq.intfID, sq.onuID, sq.uniID, sq.tpID, meterConfig); err != nil {
+ log.Error("Failed to update meter id for onu %d, meterid %d", sq.onuID, sq.meterID)
+ return err
+ }
+ log.Debugw("updated-meter-info into KV store successfully", log.Fields{"Direction": Direction,
+ "Meter": meterConfig})
+ return nil
+}
+
+func (f *OpenOltFlowMgr) pushSchedulerQueuesToDevice(ctx context.Context, sq schedQueue, TrafficShaping *tp_pb.TrafficShapingInfo, TrafficSched []*tp_pb.TrafficScheduler) error {
+
+ trafficQueues, err := f.techprofile[sq.intfID].GetTrafficQueues(sq.tpInst, sq.direction)
+
+ if err != nil {
+ log.Errorw("Unable to construct traffic queue configuration", log.Fields{"intfID": sq.intfID, "direction": sq.direction})
+ return err
+ }
+
+ log.Debugw("Sending Traffic scheduler create to device", log.Fields{"Direction": sq.direction, "TrafficScheds": TrafficSched})
+ if _, err := f.deviceHandler.Client.CreateTrafficSchedulers(ctx, &tp_pb.TrafficSchedulers{
+ IntfId: sq.intfID, OnuId: sq.onuID,
+ UniId: sq.uniID, PortNo: sq.uniPort,
+ TrafficScheds: TrafficSched}); err != nil {
+ log.Errorw("Failed to create traffic schedulers", log.Fields{"error": err})
+ return err
+ }
+
+ // On receiving the CreateTrafficQueues request, the driver should create corresponding
+ // downstream queues.
+ log.Debugw("Sending Traffic Queues create to device", log.Fields{"Direction": sq.direction, "TrafficQueues": trafficQueues})
+ if _, err := f.deviceHandler.Client.CreateTrafficQueues(ctx,
+ &tp_pb.TrafficQueues{IntfId: sq.intfID, OnuId: sq.onuID,
+ UniId: sq.uniID, PortNo: sq.uniPort,
+ TrafficQueues: trafficQueues}); err != nil {
+ log.Errorw("Failed to create traffic queues in device", log.Fields{"error": err})
+ return err
+ }
+
+ if sq.direction == tp_pb.Direction_DOWNSTREAM {
+ multicastTrafficQueues := f.techprofile[sq.intfID].GetMulticastTrafficQueues(sq.tpInst)
+ if len(multicastTrafficQueues) > 0 {
+ if _, present := f.interfaceToMcastQueueMap[sq.intfID]; !present {
+ //assumed that there is only one queue per PON for the multicast service
+ //the default queue with multicastQueuePerPonPort.Priority per a pon interface is used for multicast service
+ //just put it in interfaceToMcastQueueMap to use for building group members
+ multicastQueuePerPonPort := multicastTrafficQueues[0]
+ f.interfaceToMcastQueueMap[sq.intfID] = &queueInfoBrief{
+ gemPortID: multicastQueuePerPonPort.GemportId,
+ servicePriority: multicastQueuePerPonPort.Priority,
+ }
+ //also store the queue info in kv store
+ f.resourceMgr.AddMcastQueueForIntf(ctx, sq.intfID,
+ multicastQueuePerPonPort.GemportId,
+ multicastQueuePerPonPort.Priority)
+ }
+ }
+ }
+ return nil
+}
+
+// RemoveSchedulerQueues removes the traffic schedulers from the device based on the given scheduler configuration and traffic shaping info
+func (f *OpenOltFlowMgr) RemoveSchedulerQueues(ctx context.Context, sq schedQueue) error {
+
+ var Direction string
+ var SchedCfg *tp_pb.SchedulerConfig
+ var err error
+ log.Debugw("Removing schedulers and Queues in OLT", log.Fields{"Direction": sq.direction, "IntfID": sq.intfID,
+ "OnuID": sq.onuID, "UniID": sq.uniID, "UniPort": sq.uniPort})
+ if sq.direction == tp_pb.Direction_UPSTREAM {
+ SchedCfg, err = f.techprofile[sq.intfID].GetUsScheduler(sq.tpInst)
+ Direction = "upstream"
+ } else if sq.direction == tp_pb.Direction_DOWNSTREAM {
+ SchedCfg, err = f.techprofile[sq.intfID].GetDsScheduler(sq.tpInst)
+ Direction = "downstream"
+ }
+
+ if err != nil {
+ log.Errorw("Unable to get Scheduler config", log.Fields{"IntID": sq.intfID, "Direction": sq.direction, "Error": err})
+ return err
+ }
+
+ KVStoreMeter, err := f.resourceMgr.GetMeterIDForOnu(ctx, Direction, sq.intfID, sq.onuID, sq.uniID, sq.tpID)
+ if err != nil {
+ log.Errorf("Failed to get Meter for Onu %d", sq.onuID)
+ return err
+ }
+ if KVStoreMeter == nil {
+ log.Debugw("No-meter-has-been-installed-yet", log.Fields{"direction": Direction, "IntfID": sq.intfID, "OnuID": sq.onuID, "UniID": sq.uniID})
+ return nil
+ }
+ cir := KVStoreMeter.Bands[0].Rate
+ cbs := KVStoreMeter.Bands[0].BurstSize
+ eir := KVStoreMeter.Bands[1].Rate
+ ebs := KVStoreMeter.Bands[1].BurstSize
+ pir := cir + eir
+ pbs := cbs + ebs
+
+ TrafficShaping := &tp_pb.TrafficShapingInfo{Cir: cir, Cbs: cbs, Pir: pir, Pbs: pbs}
+
+ TrafficSched := []*tp_pb.TrafficScheduler{f.techprofile[sq.intfID].GetTrafficScheduler(sq.tpInst, SchedCfg, TrafficShaping)}
+
+ TrafficQueues, err := f.techprofile[sq.intfID].GetTrafficQueues(sq.tpInst, sq.direction)
+ if err != nil {
+ log.Errorw("Unable to construct traffic queue configuration", log.Fields{"intfID": sq.intfID, "direction": sq.direction})
+ return err
+ }
+
+ if _, err = f.deviceHandler.Client.RemoveTrafficQueues(ctx,
+ &tp_pb.TrafficQueues{IntfId: sq.intfID, OnuId: sq.onuID,
+ UniId: sq.uniID, PortNo: sq.uniPort,
+ TrafficQueues: TrafficQueues}); err != nil {
+ log.Errorw("Failed to remove traffic queues", log.Fields{"error": err})
+ return err
+ }
+ log.Debug("Removed traffic queues successfully")
+ if _, err = f.deviceHandler.Client.RemoveTrafficSchedulers(ctx, &tp_pb.TrafficSchedulers{
+ IntfId: sq.intfID, OnuId: sq.onuID,
+ UniId: sq.uniID, PortNo: sq.uniPort,
+ TrafficScheds: TrafficSched}); err != nil {
+ log.Errorw("failed to remove traffic schedulers", log.Fields{"error": err})
+ return err
+ }
+
+ log.Debug("Removed traffic schedulers successfully")
+
+ /* After we successfully remove the scheduler configuration on the OLT device,
+ * delete the meter id on the KV store.
+ */
+ err = f.resourceMgr.RemoveMeterIDForOnu(ctx, Direction, sq.intfID, sq.onuID, sq.uniID, sq.tpID)
+ if err != nil {
+ log.Errorf("Failed to remove meter for onu %d, meter id %d", sq.onuID, KVStoreMeter.MeterId)
+ return err
+ }
+ log.Debugw("Removed-meter-from-KV-store successfully", log.Fields{"MeterId": KVStoreMeter.MeterId, "dir": Direction})
+ return err
+}
+
+// This function allocates tconts and GEM ports for an ONU
+func (f *OpenOltFlowMgr) createTcontGemports(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, uni string, uniPort uint32, TpID uint32, UsMeterID uint32, DsMeterID uint32, flowMetadata *voltha.FlowMetadata) (uint32, []uint32, *tp.TechProfile) {
+ var allocIDs []uint32
+ var allgemPortIDs []uint32
+ var gemPortIDs []uint32
+ tpInstanceExists := false
+ var err error
+
+ allocIDs = f.resourceMgr.GetCurrentAllocIDsForOnu(ctx, intfID, onuID, uniID)
+ allgemPortIDs = f.resourceMgr.GetCurrentGEMPortIDsForOnu(ctx, intfID, onuID, uniID)
+
+ tpPath := f.getTPpath(intfID, uni, TpID)
+
+ log.Infow("creating-new-tcont-and-gem", log.Fields{"pon": intfID, "onu": onuID, "uni": uniID})
+
+ // Check tech profile instance already exists for derived port name
+ techProfileInstance, _ := f.techprofile[intfID].GetTPInstanceFromKVStore(ctx, TpID, tpPath)
+ if techProfileInstance == nil {
+ log.Infow("tp-instance-not-found--creating-new", log.Fields{"path": tpPath})
+ techProfileInstance, err = f.techprofile[intfID].CreateTechProfInstance(ctx, TpID, uni, intfID)
+ if err != nil {
+ // This should not happen, something wrong in KV backend transaction
+ log.Errorw("tp-instance-create-failed", log.Fields{"error": err, "tpID": TpID})
+ return 0, nil, nil
+ }
+ f.resourceMgr.UpdateTechProfileIDForOnu(ctx, intfID, onuID, uniID, TpID)
+ } else {
+ log.Debugw("Tech-profile-instance-already-exist-for-given port-name", log.Fields{"uni": uni})
+ tpInstanceExists = true
+ }
+ if UsMeterID != 0 {
+ sq := schedQueue{direction: tp_pb.Direction_UPSTREAM, intfID: intfID, onuID: onuID, uniID: uniID, tpID: TpID,
+ uniPort: uniPort, tpInst: techProfileInstance, meterID: UsMeterID, flowMetadata: flowMetadata}
+ if err := f.CreateSchedulerQueues(ctx, sq); err != nil {
+ log.Errorw("CreateSchedulerQueues Failed-upstream", log.Fields{"error": err, "meterID": UsMeterID})
+ return 0, nil, nil
+ }
+ }
+ if DsMeterID != 0 {
+ sq := schedQueue{direction: tp_pb.Direction_DOWNSTREAM, intfID: intfID, onuID: onuID, uniID: uniID, tpID: TpID,
+ uniPort: uniPort, tpInst: techProfileInstance, meterID: DsMeterID, flowMetadata: flowMetadata}
+ if err := f.CreateSchedulerQueues(ctx, sq); err != nil {
+ log.Errorw("CreateSchedulerQueues Failed-downstream", log.Fields{"error": err, "meterID": DsMeterID})
+ return 0, nil, nil
+ }
+ }
+
+ allocID := techProfileInstance.UsScheduler.AllocID
+ for _, gem := range techProfileInstance.UpstreamGemPortAttributeList {
+ gemPortIDs = append(gemPortIDs, gem.GemportID)
+ }
+
+ if tpInstanceExists {
+ return allocID, gemPortIDs, techProfileInstance
+ }
+
+ allocIDs = appendUnique(allocIDs, allocID)
+ for _, gemPortID := range gemPortIDs {
+ allgemPortIDs = appendUnique(allgemPortIDs, gemPortID)
+ }
+
+ log.Debugw("Allocated Tcont and GEM ports", log.Fields{"allocIDs": allocIDs, "gemports": allgemPortIDs})
+ // Send Tconts and GEM ports to KV store
+ f.storeTcontsGEMPortsIntoKVStore(ctx, intfID, onuID, uniID, allocIDs, allgemPortIDs)
+ return allocID, gemPortIDs, techProfileInstance
+}
+
+func (f *OpenOltFlowMgr) storeTcontsGEMPortsIntoKVStore(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, allocID []uint32, gemPortIDs []uint32) {
+
+ log.Debugw("Storing allocated Tconts and GEM ports into KV store",
+ log.Fields{"intfId": intfID, "onuId": onuID, "uniId": uniID, "allocID": allocID, "gemPortIDs": gemPortIDs})
+ /* Update the allocated alloc_id and gem_port_id for the ONU/UNI to KV store */
+ if err := f.resourceMgr.UpdateAllocIdsForOnu(ctx, intfID, onuID, uniID, allocID); err != nil {
+ log.Error("Errow while uploading allocID to KV store")
+ }
+ if err := f.resourceMgr.UpdateGEMPortIDsForOnu(ctx, intfID, onuID, uniID, gemPortIDs); err != nil {
+ log.Error("Errow while uploading GEMports to KV store")
+ }
+ if err := f.resourceMgr.UpdateGEMportsPonportToOnuMapOnKVStore(ctx, gemPortIDs, intfID, onuID, uniID); err != nil {
+ log.Error("Errow while uploading gemtopon map to KV store")
+ }
+ log.Debug("Stored tconts and GEM into KV store successfully")
+ for _, gemPort := range gemPortIDs {
+ f.addGemPortToOnuInfoMap(ctx, intfID, onuID, gemPort)
+ }
+}
+
+func (f *OpenOltFlowMgr) populateTechProfilePerPonPort() error {
+ var tpCount int
+ for _, techRange := range f.resourceMgr.DevInfo.Ranges {
+ for _, intfID := range techRange.IntfIds {
+ f.techprofile[intfID] = f.resourceMgr.ResourceMgrs[uint32(intfID)].TechProfileMgr
+ tpCount++
+ log.Debugw("Init tech profile done", log.Fields{"intfID": intfID})
+ }
+ }
+ //Make sure we have as many tech_profiles as there are pon ports on the device
+ if tpCount != int(f.resourceMgr.DevInfo.GetPonPorts()) {
+ return NewErrInvalidValue(log.Fields{
+ "reason": "TP count does not match number of PON ports",
+ "tech-profile-count": tpCount,
+ "pon-port-count": f.resourceMgr.DevInfo.GetPonPorts()}, nil).Log()
+ }
+ log.Infow("Populated techprofile for ponports successfully",
+ log.Fields{"numofTech": tpCount, "numPonPorts": f.resourceMgr.DevInfo.GetPonPorts()})
+ return nil
+}
+
+func (f *OpenOltFlowMgr) addUpstreamDataFlow(ctx context.Context, intfID uint32, onuID uint32, uniID uint32,
+ portNo uint32, uplinkClassifier map[string]interface{},
+ uplinkAction map[string]interface{}, logicalFlow *ofp.OfpFlowStats,
+ allocID uint32, gemportID uint32) error {
+ uplinkClassifier[PacketTagType] = SingleTag
+ log.Debugw("Adding upstream data flow", log.Fields{"uplinkClassifier": uplinkClassifier, "uplinkAction": uplinkAction})
+ return f.addHSIAFlow(ctx, intfID, onuID, uniID, portNo, uplinkClassifier, uplinkAction,
+ Upstream, logicalFlow, allocID, gemportID)
+ /* TODO: Install Secondary EAP on the subscriber vlan */
+}
+
+func (f *OpenOltFlowMgr) addDownstreamDataFlow(ctx context.Context, intfID uint32, onuID uint32, uniID uint32,
+ portNo uint32, downlinkClassifier map[string]interface{},
+ downlinkAction map[string]interface{}, logicalFlow *ofp.OfpFlowStats,
+ allocID uint32, gemportID uint32) error {
+ downlinkClassifier[PacketTagType] = DoubleTag
+ log.Debugw("Adding downstream data flow", log.Fields{"downlinkClassifier": downlinkClassifier,
+ "downlinkAction": downlinkAction})
+ // Ignore Downlink trap flow given by core, cannot do anything with this flow */
+ if vlan, exists := downlinkClassifier[VlanVid]; exists {
+ if vlan.(uint32) == (uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 4000) { //private VLAN given by core
+ if metadata, exists := downlinkClassifier[Metadata]; exists { // inport is filled in metadata by core
+ if uint32(metadata.(uint64)) == MkUniPortNum(intfID, onuID, uniID) {
+ log.Infow("Ignoring DL trap device flow from core", log.Fields{"flow": logicalFlow})
+ return nil
+ }
+ }
+ }
+ }
+
+ /* Already this info available classifier? */
+ downlinkAction[PopVlan] = true
+ // vlan_vid is a uint32. must be type asserted as such or conversion fails
+ dlClVid, ok := downlinkClassifier[VlanVid].(uint32)
+ if ok {
+ downlinkAction[VlanVid] = dlClVid & 0xfff
+ } else {
+ return NewErrInvalidValue(log.Fields{
+ "reason": "failed to convert VLANID classifier",
+ "vlan-id": VlanVid}, nil).Log()
+ }
+
+ return f.addHSIAFlow(ctx, intfID, onuID, uniID, portNo, downlinkClassifier, downlinkAction,
+ Downstream, logicalFlow, allocID, gemportID)
+}
+
+func (f *OpenOltFlowMgr) addHSIAFlow(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, portNo uint32, classifier map[string]interface{},
+ action map[string]interface{}, direction string, logicalFlow *ofp.OfpFlowStats,
+ allocID uint32, gemPortID uint32) error {
+ /* One of the OLT platform (Broadcom BAL) requires that symmetric
+ flows require the same flow_id to be used across UL and DL.
+ Since HSIA flow is the only symmetric flow currently, we need to
+ re-use the flow_id across both direction. The 'flow_category'
+ takes priority over flow_cookie to find any available HSIA_FLOW
+ id for the ONU.
+ */
+ log.Debugw("Adding HSIA flow", log.Fields{"intfId": intfID, "onuId": onuID, "uniId": uniID, "classifier": classifier,
+ "action": action, "direction": direction, "allocId": allocID, "gemPortId": gemPortID,
+ "logicalFlow": *logicalFlow})
+ var vlanPbit uint32 = 0xff // means no pbit
+ if _, ok := classifier[VlanPcp]; ok {
+ vlanPbit = classifier[VlanPcp].(uint32)
+ log.Debugw("Found pbit in the flow", log.Fields{"VlanPbit": vlanPbit})
+ } else {
+ log.Debugw("pbit-not-found-in-flow", log.Fields{"vlan-pcp": VlanPcp})
+ }
+ flowStoreCookie := getFlowStoreCookie(classifier, gemPortID)
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(intfID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ log.Debug("flow-already-exists")
+ return nil
+ }
+ flowID, err := f.resourceMgr.GetFlowID(ctx, intfID, int32(onuID), int32(uniID), gemPortID, flowStoreCookie, HsiaFlow, vlanPbit)
+ if err != nil {
+ return NewErrNotFound("hsia-flow-id", log.Fields{"direction": direction}, err).Log()
+ }
+ classifierProto, err := makeOpenOltClassifierField(classifier)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"classifier": classifier}, err).Log()
+ }
+ log.Debugw("Created classifier proto", log.Fields{"classifier": *classifierProto})
+ actionProto, err := makeOpenOltActionField(action)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"action": action}, err).Log()
+ }
+ log.Debugw("Created action proto", log.Fields{"action": *actionProto})
+ networkIntfID, err := getNniIntfID(classifier, action)
+ if err != nil {
+ return NewErrNotFound("nni-interface-id",
+ log.Fields{
+ "classifier": classifier,
+ "action": action,
+ }, err).Log()
+ }
+ flow := openoltpb2.Flow{AccessIntfId: int32(intfID),
+ OnuId: int32(onuID),
+ UniId: int32(uniID),
+ FlowId: flowID,
+ FlowType: direction,
+ AllocId: int32(allocID),
+ NetworkIntfId: int32(networkIntfID),
+ GemportId: int32(gemPortID),
+ Classifier: classifierProto,
+ Action: actionProto,
+ Priority: int32(logicalFlow.Priority),
+ Cookie: logicalFlow.Cookie,
+ PortNo: portNo}
+ if err := f.addFlowToDevice(ctx, logicalFlow, &flow); err != nil {
+ return NewErrFlowOp("add", flowID, nil, err).Log()
+ }
+ log.Debug("HSIA flow added to device successfully", log.Fields{"direction": direction})
+ flowsToKVStore := f.getUpdatedFlowInfo(ctx, &flow, flowStoreCookie, HsiaFlow, flowID, logicalFlow.Id)
+ if err := f.updateFlowInfoToKVStore(ctx, flow.AccessIntfId,
+ flow.OnuId,
+ flow.UniId,
+ flow.FlowId /*flowCategory,*/, flowsToKVStore); err != nil {
+ return NewErrPersistence("update", "flow", flowID, log.Fields{"flow": flow}, err).Log()
+ }
+ return nil
+}
+
+func (f *OpenOltFlowMgr) addDHCPTrapFlow(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, portNo uint32, classifier map[string]interface{}, action map[string]interface{}, logicalFlow *ofp.OfpFlowStats, allocID uint32, gemPortID uint32) error {
+
+ networkIntfID, err := getNniIntfID(classifier, action)
+ if err != nil {
+ return NewErrNotFound("nni-interface-id", log.Fields{
+ "classifier": classifier,
+ "action": action},
+ err).Log()
+ }
+
+ // Clear the action map
+ for k := range action {
+ delete(action, k)
+ }
+
+ action[TrapToHost] = true
+ classifier[UDPSrc] = uint32(68)
+ classifier[UDPDst] = uint32(67)
+ classifier[PacketTagType] = SingleTag
+ delete(classifier, VlanVid)
+
+ flowStoreCookie := getFlowStoreCookie(classifier, gemPortID)
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(intfID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ log.Debug("Flow-exists--not-re-adding")
+ return nil
+ }
+
+ flowID, err := f.resourceMgr.GetFlowID(ctx, intfID, int32(onuID), int32(uniID), gemPortID, flowStoreCookie, DhcpFlow, 0 /*classifier[VLAN_PCP].(uint32)*/)
+
+ if err != nil {
+ return NewErrNotFound("flow", log.Fields{
+ "interface-id": intfID,
+ "gem-port": gemPortID,
+ "cookie": flowStoreCookie},
+ err).Log()
+ }
+
+ log.Debugw("Creating UL DHCP flow", log.Fields{"ul_classifier": classifier, "ul_action": action, "uplinkFlowId": flowID})
+
+ classifierProto, err := makeOpenOltClassifierField(classifier)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"classifier": classifier}, err).Log()
+ }
+ log.Debugw("Created classifier proto", log.Fields{"classifier": *classifierProto})
+ actionProto, err := makeOpenOltActionField(action)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"action": action}, err).Log()
+ }
+
+ dhcpFlow := openoltpb2.Flow{AccessIntfId: int32(intfID),
+ OnuId: int32(onuID),
+ UniId: int32(uniID),
+ FlowId: flowID,
+ FlowType: Upstream,
+ AllocId: int32(allocID),
+ NetworkIntfId: int32(networkIntfID),
+ GemportId: int32(gemPortID),
+ Classifier: classifierProto,
+ Action: actionProto,
+ Priority: int32(logicalFlow.Priority),
+ Cookie: logicalFlow.Cookie,
+ PortNo: portNo}
+
+ if err := f.addFlowToDevice(ctx, logicalFlow, &dhcpFlow); err != nil {
+ return NewErrFlowOp("add", flowID, log.Fields{"dhcp-flow": dhcpFlow}, err).Log()
+ }
+ log.Debug("DHCP UL flow added to device successfully")
+ flowsToKVStore := f.getUpdatedFlowInfo(ctx, &dhcpFlow, flowStoreCookie, "DHCP", flowID, logicalFlow.Id)
+ if err := f.updateFlowInfoToKVStore(ctx, dhcpFlow.AccessIntfId,
+ dhcpFlow.OnuId,
+ dhcpFlow.UniId,
+ dhcpFlow.FlowId, flowsToKVStore); err != nil {
+ return NewErrPersistence("update", "flow", dhcpFlow.FlowId, log.Fields{"flow": dhcpFlow}, err).Log()
+ }
+
+ return nil
+}
+
+//addIGMPTrapFlow creates IGMP trap-to-host flow
+func (f *OpenOltFlowMgr) addIGMPTrapFlow(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, portNo uint32, classifier map[string]interface{},
+ action map[string]interface{}, logicalFlow *ofp.OfpFlowStats, allocID uint32, gemPortID uint32) error {
+ return f.addUpstreamTrapFlow(ctx, intfID, onuID, uniID, portNo, classifier, action, logicalFlow, allocID, gemPortID, IgmpFlow)
+}
+
+//addUpstreamTrapFlow creates a trap-to-host flow
+func (f *OpenOltFlowMgr) addUpstreamTrapFlow(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, portNo uint32, classifier map[string]interface{},
+ action map[string]interface{}, logicalFlow *ofp.OfpFlowStats, allocID uint32, gemPortID uint32, flowType string) error {
+
+ networkIntfID, err := getNniIntfID(classifier, action)
+ if err != nil {
+ return NewErrNotFound("nni-interface-id", log.Fields{
+ "classifier": classifier,
+ "action": action},
+ err).Log()
+ }
+
+ // Clear the action map
+ for k := range action {
+ delete(action, k)
+ }
+
+ action[TrapToHost] = true
+ classifier[PacketTagType] = SingleTag
+ delete(classifier, VlanVid)
+
+ flowStoreCookie := getFlowStoreCookie(classifier, gemPortID)
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(networkIntfID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ log.Debug("Flow-exists-not-re-adding")
+ return nil
+ }
+
+ flowID, err := f.resourceMgr.GetFlowID(ctx, intfID, int32(onuID), int32(uniID), gemPortID, flowStoreCookie, flowType, 0, 0 /*classifier[VLAN_PCP].(uint32)*/)
+
+ if err != nil {
+ return NewErrNotFound("flow-id", log.Fields{
+ "interface-id": intfID,
+ "oni-id": onuID,
+ "cookie": flowStoreCookie,
+ "flow-type": flowType},
+ err).Log()
+ }
+
+ log.Debugw("Creating upstream trap flow", log.Fields{"ul_classifier": classifier, "ul_action": action, "uplinkFlowId": flowID, "flowType": flowType})
+
+ classifierProto, err := makeOpenOltClassifierField(classifier)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"classifier": classifier}, err).Log()
+ }
+ log.Debugw("Created classifier proto", log.Fields{"classifier": *classifierProto})
+ actionProto, err := makeOpenOltActionField(action)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"action": action}, err).Log()
+ }
+
+ flow := openoltpb2.Flow{AccessIntfId: int32(intfID),
+ OnuId: int32(onuID),
+ UniId: int32(uniID),
+ FlowId: flowID,
+ FlowType: Upstream,
+ AllocId: int32(allocID),
+ NetworkIntfId: int32(networkIntfID),
+ GemportId: int32(gemPortID),
+ Classifier: classifierProto,
+ Action: actionProto,
+ Priority: int32(logicalFlow.Priority),
+ Cookie: logicalFlow.Cookie,
+ PortNo: portNo}
+
+ if err := f.addFlowToDevice(ctx, logicalFlow, &flow); err != nil {
+ return NewErrFlowOp("add", flowID, log.Fields{"flow": flow}, err).Log()
+ }
+ log.Debugf("%s UL flow added to device successfully", flowType)
+
+ flowsToKVStore := f.getUpdatedFlowInfo(ctx, &flow, flowStoreCookie, flowType, flowID, logicalFlow.Id)
+ if err := f.updateFlowInfoToKVStore(ctx, flow.AccessIntfId,
+ flow.OnuId,
+ flow.UniId,
+ flow.FlowId, flowsToKVStore); err != nil {
+ return NewErrPersistence("update", "flow", flow.FlowId, log.Fields{"flow": flow}, err).Log()
+ }
+
+ return nil
+}
+
+// Add EAPOL flow to device with mac, vlanId as classifier for upstream and downstream
+func (f *OpenOltFlowMgr) addEAPOLFlow(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, portNo uint32, classifier map[string]interface{}, action map[string]interface{}, logicalFlow *ofp.OfpFlowStats, allocID uint32, gemPortID uint32, vlanID uint32) error {
+ log.Debugw("Adding EAPOL to device", log.Fields{"intfId": intfID, "onuId": onuID, "portNo": portNo, "allocId": allocID, "gemPortId": gemPortID, "vlanId": vlanID, "flow": logicalFlow})
+
+ uplinkClassifier := make(map[string]interface{})
+ uplinkAction := make(map[string]interface{})
+
+ // Fill Classfier
+ uplinkClassifier[EthType] = uint32(EapEthType)
+ uplinkClassifier[PacketTagType] = SingleTag
+ uplinkClassifier[VlanVid] = vlanID
+ // Fill action
+ uplinkAction[TrapToHost] = true
+ flowStoreCookie := getFlowStoreCookie(uplinkClassifier, gemPortID)
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(intfID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ log.Debug("Flow-exists-not-re-adding")
+ return nil
+ }
+ //Add Uplink EAPOL Flow
+ uplinkFlowID, err := f.resourceMgr.GetFlowID(ctx, intfID, int32(onuID), int32(uniID), gemPortID, flowStoreCookie, "", 0)
+ if err != nil {
+ return NewErrNotFound("flow-id", log.Fields{
+ "interface-id": intfID,
+ "onu-id": onuID,
+ "coookie": flowStoreCookie},
+ err).Log()
+ }
+ log.Debugw("Creating UL EAPOL flow", log.Fields{"ul_classifier": uplinkClassifier, "ul_action": uplinkAction, "uplinkFlowId": uplinkFlowID})
+
+ classifierProto, err := makeOpenOltClassifierField(uplinkClassifier)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"classifier": uplinkClassifier}, err).Log()
+ }
+ log.Debugw("Created classifier proto", log.Fields{"classifier": *classifierProto})
+ actionProto, err := makeOpenOltActionField(uplinkAction)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"action": uplinkAction}, err).Log()
+ }
+ log.Debugw("Created action proto", log.Fields{"action": *actionProto})
+ networkIntfID, err := getNniIntfID(classifier, action)
+ if err != nil {
+ return NewErrNotFound("nni-interface-id", log.Fields{
+ "classifier": classifier,
+ "action": action},
+ err).Log()
+ }
+
+ upstreamFlow := openoltpb2.Flow{AccessIntfId: int32(intfID),
+ OnuId: int32(onuID),
+ UniId: int32(uniID),
+ FlowId: uplinkFlowID,
+ FlowType: Upstream,
+ AllocId: int32(allocID),
+ NetworkIntfId: int32(networkIntfID),
+ GemportId: int32(gemPortID),
+ Classifier: classifierProto,
+ Action: actionProto,
+ Priority: int32(logicalFlow.Priority),
+ Cookie: logicalFlow.Cookie,
+ PortNo: portNo}
+ if err := f.addFlowToDevice(ctx, logicalFlow, &upstreamFlow); err != nil {
+ return NewErrFlowOp("add", uplinkFlowID, log.Fields{"flow": upstreamFlow}, err).Log()
+ }
+ log.Debug("EAPOL UL flow added to device successfully")
+ flowCategory := "EAPOL"
+ flowsToKVStore := f.getUpdatedFlowInfo(ctx, &upstreamFlow, flowStoreCookie, flowCategory, uplinkFlowID, logicalFlow.Id)
+ if err := f.updateFlowInfoToKVStore(ctx, upstreamFlow.AccessIntfId,
+ upstreamFlow.OnuId,
+ upstreamFlow.UniId,
+ upstreamFlow.FlowId,
+ /* lowCategory, */
+ flowsToKVStore); err != nil {
+ return NewErrPersistence("update", "flow", upstreamFlow.FlowId, log.Fields{"flow": upstreamFlow}, err).Log()
+ }
+
+ log.Debugw("Added EAPOL flows to device successfully", log.Fields{"flow": logicalFlow})
+ return nil
+}
+
+func makeOpenOltClassifierField(classifierInfo map[string]interface{}) (*openoltpb2.Classifier, error) {
+ var classifier openoltpb2.Classifier
+
+ classifier.EthType, _ = classifierInfo[EthType].(uint32)
+ classifier.IpProto, _ = classifierInfo[IPProto].(uint32)
+ if vlanID, ok := classifierInfo[VlanVid].(uint32); ok {
+ vid := vlanID & VlanvIDMask
+ if vid != ReservedVlan {
+ classifier.OVid = vid
+ }
+ }
+ if metadata, ok := classifierInfo[Metadata].(uint64); ok {
+ vid := uint32(metadata)
+ if vid != ReservedVlan {
+ classifier.IVid = vid
+ }
+ }
+ // Use VlanPCPMask (0xff) to signify NO PCP. Else use valid PCP (0 to 7)
+ if vlanPcp, ok := classifierInfo[VlanPcp].(uint32); ok {
+ classifier.OPbits = vlanPcp
+ } else {
+ classifier.OPbits = VlanPCPMask
+ }
+ classifier.SrcPort, _ = classifierInfo[UDPSrc].(uint32)
+ classifier.DstPort, _ = classifierInfo[UDPDst].(uint32)
+ classifier.DstIp, _ = classifierInfo[Ipv4Dst].(uint32)
+ classifier.SrcIp, _ = classifierInfo[Ipv4Src].(uint32)
+ classifier.DstMac, _ = classifierInfo[EthDst].([]uint8)
+ if pktTagType, ok := classifierInfo[PacketTagType].(string); ok {
+ classifier.PktTagType = pktTagType
+
+ switch pktTagType {
+ case SingleTag:
+ case DoubleTag:
+ case Untagged:
+ default:
+ return nil, NewErrInvalidValue(log.Fields{"packet-tag-type": pktTagType}, nil).Log()
+ }
+ }
+ return &classifier, nil
+}
+
+func makeOpenOltActionField(actionInfo map[string]interface{}) (*openoltpb2.Action, error) {
+ var actionCmd openoltpb2.ActionCmd
+ var action openoltpb2.Action
+ action.Cmd = &actionCmd
+ if _, ok := actionInfo[PopVlan]; ok {
+ action.OVid = actionInfo[VlanVid].(uint32)
+ action.Cmd.RemoveOuterTag = true
+ } else if _, ok := actionInfo[PushVlan]; ok {
+ action.OVid = actionInfo[VlanVid].(uint32)
+ action.Cmd.AddOuterTag = true
+ } else if _, ok := actionInfo[TrapToHost]; ok {
+ action.Cmd.TrapToHost = actionInfo[TrapToHost].(bool)
+ } else {
+ return nil, NewErrInvalidValue(log.Fields{"action-command": actionInfo}, nil).Log()
+ }
+ return &action, nil
+}
+
+func (f *OpenOltFlowMgr) getTPpath(intfID uint32, uni string, TpID uint32) string {
+ return f.techprofile[intfID].GetTechProfileInstanceKVPath(TpID, uni)
+}
+
+// DeleteTechProfileInstances removes the tech profile instances from persistent storage
+func (f *OpenOltFlowMgr) DeleteTechProfileInstances(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, sn string) error {
+ tpIDList := f.resourceMgr.GetTechProfileIDForOnu(ctx, intfID, onuID, uniID)
+ uniPortName := fmt.Sprintf("pon-{%d}/onu-{%d}/uni-{%d}", intfID, onuID, uniID)
+ for _, tpID := range tpIDList {
+ if err := f.DeleteTechProfileInstance(ctx, intfID, onuID, uniID, uniPortName, tpID); err != nil {
+ log.Debugw("Failed-to-delete-tp-instance-from-kv-store", log.Fields{"tp-id": tpID, "uni-port-name": uniPortName})
+ // return err
+ // We should continue to delete tech-profile instances for other TP IDs
+ }
+ }
+ return nil
+}
+
+// DeleteTechProfileInstance removes the tech profile instance from persistent storage
+func (f *OpenOltFlowMgr) DeleteTechProfileInstance(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, uniPortName string, tpID uint32) error {
+ if uniPortName == "" {
+ uniPortName = fmt.Sprintf("pon-{%d}/onu-{%d}/uni-{%d}", intfID, onuID, uniID)
+ }
+ if err := f.techprofile[intfID].DeleteTechProfileInstance(ctx, tpID, uniPortName); err != nil {
+ log.Debugw("Failed-to-delete-tp-instance-from-kv-store", log.Fields{"tp-id": tpID, "uni-port-name": uniPortName})
+ return err
+ }
+ return nil
+}
+
+func getFlowStoreCookie(classifier map[string]interface{}, gemPortID uint32) uint64 {
+ if len(classifier) == 0 { // should never happen
+ log.Error("Invalid classfier object")
+ return 0
+ }
+ log.Debugw("generating flow store cookie", log.Fields{"classifier": classifier, "gemPortID": gemPortID})
+ var jsonData []byte
+ var flowString string
+ var err error
+ // TODO: Do we need to marshall ??
+ if jsonData, err = json.Marshal(classifier); err != nil {
+ log.Error("Failed to encode classifier")
+ return 0
+ }
+ flowString = string(jsonData)
+ if gemPortID != 0 {
+ flowString = fmt.Sprintf("%s%s", string(jsonData), string(gemPortID))
+ }
+ h := md5.New()
+ _, _ = h.Write([]byte(flowString))
+ hash := big.NewInt(0)
+ hash.SetBytes(h.Sum(nil))
+ generatedHash := hash.Uint64()
+ log.Debugw("hash generated", log.Fields{"hash": generatedHash})
+ return generatedHash
+}
+
+func (f *OpenOltFlowMgr) getUpdatedFlowInfo(ctx context.Context, flow *openoltpb2.Flow, flowStoreCookie uint64, flowCategory string, deviceFlowID uint32, logicalFlowID uint64) *[]rsrcMgr.FlowInfo {
+ var flows = []rsrcMgr.FlowInfo{{Flow: flow, FlowCategory: flowCategory, FlowStoreCookie: flowStoreCookie, LogicalFlowID: logicalFlowID}}
+ var intfID uint32
+ /* For flows which trap out of the NNI, the AccessIntfId is invalid
+ (set to -1). In such cases, we need to refer to the NetworkIntfId .
+ */
+ if flow.AccessIntfId != -1 {
+ intfID = uint32(flow.AccessIntfId)
+ } else {
+ intfID = uint32(flow.NetworkIntfId)
+ }
+ // Get existing flows matching flowid for given subscriber from KV store
+ existingFlows := f.resourceMgr.GetFlowIDInfo(ctx, intfID, flow.OnuId, flow.UniId, flow.FlowId)
+ if existingFlows != nil {
+ log.Debugw("Flow exists for given flowID, appending it to current flow", log.Fields{"flowID": flow.FlowId})
+ //for _, f := range *existingFlows {
+ // flows = append(flows, f)
+ //}
+ flows = append(flows, *existingFlows...)
+ }
+ log.Debugw("Updated flows for given flowID and onuid", log.Fields{"updatedflow": flows, "flowid": flow.FlowId, "onu": flow.OnuId})
+ return &flows
+}
+
+//func (f *OpenOltFlowMgr) getUpdatedFlowInfo(flow *openolt_pb2.Flow, flowStoreCookie uint64, flowCategory string) *[]rsrcMgr.FlowInfo {
+// var flows []rsrcMgr.FlowInfo = []rsrcMgr.FlowInfo{rsrcMgr.FlowInfo{Flow: flow, FlowCategory: flowCategory, FlowStoreCookie: flowStoreCookie}}
+// var intfId uint32
+// /* For flows which trap out of the NNI, the AccessIntfId is invalid
+// (set to -1). In such cases, we need to refer to the NetworkIntfId .
+// */
+// if flow.AccessIntfId != -1 {
+// intfId = uint32(flow.AccessIntfId)
+// } else {
+// intfId = uint32(flow.NetworkIntfId)
+// }
+// // Get existing flows matching flowid for given subscriber from KV store
+// existingFlows := f.resourceMgr.GetFlowIDInfo(intfId, uint32(flow.OnuId), uint32(flow.UniId), flow.FlowId)
+// if existingFlows != nil {
+// log.Debugw("Flow exists for given flowID, appending it to current flow", log.Fields{"flowID": flow.FlowId})
+// for _, f := range *existingFlows {
+// flows = append(flows, f)
+// }
+// }
+// log.Debugw("Updated flows for given flowID and onuid", log.Fields{"updatedflow": flows, "flowid": flow.FlowId, "onu": flow.OnuId})
+// return &flows
+//}
+
+func (f *OpenOltFlowMgr) updateFlowInfoToKVStore(ctx context.Context, intfID int32, onuID int32, uniID int32, flowID uint32, flows *[]rsrcMgr.FlowInfo) error {
+ log.Debugw("Storing flow(s) into KV store", log.Fields{"flows": *flows})
+ if err := f.resourceMgr.UpdateFlowIDInfo(ctx, intfID, onuID, uniID, flowID, flows); err != nil {
+ log.Debug("Error while Storing flow into KV store")
+ return err
+ }
+ log.Info("Stored flow(s) into KV store successfully!")
+ return nil
+}
+
+func (f *OpenOltFlowMgr) addFlowToDevice(ctx context.Context, logicalFlow *ofp.OfpFlowStats, deviceFlow *openoltpb2.Flow) error {
+
+ var intfID uint32
+ /* For flows which trap out of the NNI, the AccessIntfId is invalid
+ (set to -1). In such cases, we need to refer to the NetworkIntfId .
+ */
+ if deviceFlow.AccessIntfId != -1 {
+ intfID = uint32(deviceFlow.AccessIntfId)
+ } else {
+ // REVIST : Why ponport is given as network port?
+ intfID = uint32(deviceFlow.NetworkIntfId)
+ }
+
+ log.Debugw("Sending flow to device via grpc", log.Fields{"flow": *deviceFlow})
+ _, err := f.deviceHandler.Client.FlowAdd(context.Background(), deviceFlow)
+
+ st, _ := status.FromError(err)
+ if st.Code() == codes.AlreadyExists {
+ log.Debug("Flow already exists", log.Fields{"err": err, "deviceFlow": deviceFlow})
+ return nil
+ }
+
+ if err != nil {
+ log.Errorw("Failed to Add flow to device", log.Fields{"err": err, "deviceFlow": deviceFlow})
+ f.resourceMgr.FreeFlowID(ctx, intfID, deviceFlow.OnuId, deviceFlow.UniId, deviceFlow.FlowId)
+ return err
+ }
+ if deviceFlow.GemportId != -1 {
+ // No need to register the flow if it is a trap on nni flow.
+ f.registerFlow(ctx, logicalFlow, deviceFlow)
+ }
+ log.Debugw("Flow added to device successfully ", log.Fields{"flow": *deviceFlow})
+ return nil
+}
+
+func (f *OpenOltFlowMgr) removeFlowFromDevice(deviceFlow *openoltpb2.Flow) error {
+ log.Debugw("Sending flow to device via grpc", log.Fields{"flow": *deviceFlow})
+ _, err := f.deviceHandler.Client.FlowRemove(context.Background(), deviceFlow)
+ if err != nil {
+ if f.deviceHandler.device.ConnectStatus == common.ConnectStatus_UNREACHABLE {
+ log.Warnw("Can not remove flow from device since it's unreachable", log.Fields{"err": err, "deviceFlow": deviceFlow})
+ //Assume the flow is removed
+ return nil
+ }
+ log.Errorw("Failed to Remove flow from device", log.Fields{"err": err, "deviceFlow": deviceFlow})
+ return err
+
+ }
+ log.Debugw("Flow removed from device successfully ", log.Fields{"flow": *deviceFlow})
+ return nil
+}
+
+/*func register_flow(deviceFlow *openolt_pb2.Flow, logicalFlow *ofp.OfpFlowStats){
+ //update core flows_proxy : flows_proxy.update('/', flows)
+}
+
+func generateStoredId(flowId uint32, direction string)uint32{
+
+ if direction == Upstream{
+ log.Debug("Upstream flow shifting flowid")
+ return ((0x1 << 15) | flowId)
+ }else if direction == Downstream{
+ log.Debug("Downstream flow not shifting flowid")
+ return flowId
+ }else{
+ log.Errorw("Unrecognized direction",log.Fields{"direction": direction})
+ return flowId
+ }
+}
+
+*/
+
+func (f *OpenOltFlowMgr) addLLDPFlow(ctx context.Context, flow *ofp.OfpFlowStats, portNo uint32) error {
+
+ classifierInfo := make(map[string]interface{})
+ actionInfo := make(map[string]interface{})
+
+ classifierInfo[EthType] = uint32(LldpEthType)
+ classifierInfo[PacketTagType] = Untagged
+ actionInfo[TrapToHost] = true
+
+ // LLDP flow is installed to trap LLDP packets on the NNI port.
+ // We manage flow_id resource pool on per PON port basis.
+ // Since this situation is tricky, as a hack, we pass the NNI port
+ // index (network_intf_id) as PON port Index for the flow_id resource
+ // pool. Also, there is no ONU Id available for trapping LLDP packets
+ // on NNI port, use onu_id as -1 (invalid)
+ // ****************** CAVEAT *******************
+ // This logic works if the NNI Port Id falls within the same valid
+ // range of PON Port Ids. If this doesn't work for some OLT Vendor
+ // we need to have a re-look at this.
+ // *********************************************
+
+ var onuID = -1
+ var uniID = -1
+ var gemPortID = -1
+
+ networkInterfaceID, err := IntfIDFromNniPortNum(portNo)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"nni-port-number": portNo}, err).Log()
+ }
+ var flowStoreCookie = getFlowStoreCookie(classifierInfo, uint32(0))
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ log.Debug("Flow-exists--not-re-adding")
+ return nil
+ }
+ flowID, err := f.resourceMgr.GetFlowID(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), uint32(gemPortID), flowStoreCookie, "", 0)
+
+ if err != nil {
+ return NewErrNotFound("flow-id", log.Fields{
+ "interface-id": networkInterfaceID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "gem-port-id": gemPortID,
+ "cookie": flowStoreCookie},
+ err).Log()
+ }
+ classifierProto, err := makeOpenOltClassifierField(classifierInfo)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"classifier": classifierInfo}, err).Log()
+ }
+ log.Debugw("Created classifier proto", log.Fields{"classifier": *classifierProto})
+ actionProto, err := makeOpenOltActionField(actionInfo)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"action": actionInfo}, err).Log()
+ }
+ log.Debugw("Created action proto", log.Fields{"action": *actionProto})
+
+ downstreamflow := openoltpb2.Flow{AccessIntfId: int32(-1), // AccessIntfId not required
+ OnuId: int32(onuID), // OnuId not required
+ UniId: int32(uniID), // UniId not used
+ FlowId: flowID,
+ FlowType: Downstream,
+ NetworkIntfId: int32(networkInterfaceID),
+ GemportId: int32(gemPortID),
+ Classifier: classifierProto,
+ Action: actionProto,
+ Priority: int32(flow.Priority),
+ Cookie: flow.Cookie,
+ PortNo: portNo}
+ if err := f.addFlowToDevice(ctx, flow, &downstreamflow); err != nil {
+ return NewErrFlowOp("add", flowID, log.Fields{"flow": downstreamflow}, err).Log()
+ }
+ log.Debug("LLDP trap on NNI flow added to device successfully")
+ flowsToKVStore := f.getUpdatedFlowInfo(ctx, &downstreamflow, flowStoreCookie, "", flowID, flow.Id)
+ if err := f.updateFlowInfoToKVStore(ctx, int32(networkInterfaceID),
+ int32(onuID),
+ int32(uniID),
+ flowID, flowsToKVStore); err != nil {
+ return NewErrPersistence("update", "flow", flowID, log.Fields{"flow": downstreamflow}, err).Log()
+ }
+ return nil
+}
+
+func getUniPortPath(intfID uint32, onuID int32, uniID int32) string {
+ return fmt.Sprintf("pon-{%d}/onu-{%d}/uni-{%d}", intfID, onuID, uniID)
+}
+
+//getOnuChildDevice to fetch onu
+func (f *OpenOltFlowMgr) getOnuChildDevice(intfID uint32, onuID uint32) (*voltha.Device, error) {
+ log.Debugw("GetChildDevice", log.Fields{"pon port": intfID, "onuId": onuID})
+ parentPortNo := IntfIDToPortNo(intfID, voltha.Port_PON_OLT)
+ onuDevice, err := f.deviceHandler.GetChildDevice(parentPortNo, onuID)
+ if err != nil {
+ return nil, NewErrNotFound("onu", log.Fields{
+ "interface-id": parentPortNo,
+ "onu-id": onuID},
+ err).Log()
+ }
+ log.Debugw("Successfully received child device from core", log.Fields{"child_device": *onuDevice})
+ return onuDevice, nil
+}
+
+func findNextFlow(flow *ofp.OfpFlowStats) *ofp.OfpFlowStats {
+ log.Info("unimplemented flow : %v", flow)
+ return nil
+}
+
+func (f *OpenOltFlowMgr) clearFlowsAndSchedulerForLogicalPort(childDevice *voltha.Device, logicalPort *voltha.LogicalPort) {
+ log.Info("unimplemented device %v, logicalport %v", childDevice, logicalPort)
+}
+
+func (f *OpenOltFlowMgr) decodeStoredID(id uint64) (uint64, string) {
+ if id>>15 == 0x1 {
+ return id & 0x7fff, Upstream
+ }
+ return id, Downstream
+}
+
+func (f *OpenOltFlowMgr) sendDeleteGemPortToChild(intfID uint32, onuID uint32, uniID uint32, gemPortID uint32, tpPath string) error {
+ onuDevice, err := f.getOnuChildDevice(intfID, onuID)
+ if err != nil {
+ log.Errorw("error fetching child device from core", log.Fields{"onuId": onuID})
+ return err
+ }
+
+ delGemPortMsg := &ic.InterAdapterDeleteGemPortMessage{UniId: uniID, TpPath: tpPath, GemPortId: gemPortID}
+ log.Debugw("sending gem port delete to openonu adapter", log.Fields{"msg": *delGemPortMsg})
+ if sendErr := f.deviceHandler.AdapterProxy.SendInterAdapterMessage(context.Background(),
+ delGemPortMsg,
+ ic.InterAdapterMessageType_DELETE_GEM_PORT_REQUEST,
+ f.deviceHandler.deviceType,
+ onuDevice.Type,
+ onuDevice.Id,
+ onuDevice.ProxyAddress.DeviceId, ""); sendErr != nil {
+ log.Errorw("failure sending del gem port to onu adapter", log.Fields{"fromAdapter": f.deviceHandler.deviceType,
+ "toAdapter": onuDevice.Type, "onuId": onuDevice.Id,
+ "proxyDeviceId": onuDevice.ProxyAddress.DeviceId})
+ return sendErr
+ }
+ log.Debugw("success sending del gem port to onu adapter", log.Fields{"msg": delGemPortMsg})
+ return nil
+}
+
+func (f *OpenOltFlowMgr) sendDeleteTcontToChild(intfID uint32, onuID uint32, uniID uint32, allocID uint32, tpPath string) error {
+ onuDevice, err := f.getOnuChildDevice(intfID, onuID)
+ if err != nil {
+ log.Errorw("error fetching child device from core", log.Fields{"onuId": onuID})
+ return err
+ }
+
+ delTcontMsg := &ic.InterAdapterDeleteTcontMessage{UniId: uniID, TpPath: tpPath, AllocId: allocID}
+ log.Debugw("sending tcont delete to openonu adapter", log.Fields{"msg": *delTcontMsg})
+ if sendErr := f.deviceHandler.AdapterProxy.SendInterAdapterMessage(context.Background(),
+ delTcontMsg,
+ ic.InterAdapterMessageType_DELETE_TCONT_REQUEST,
+ f.deviceHandler.deviceType,
+ onuDevice.Type,
+ onuDevice.Id,
+ onuDevice.ProxyAddress.DeviceId, ""); sendErr != nil {
+ log.Errorw("failure sending del tcont to onu adapter", log.Fields{"fromAdapter": f.deviceHandler.deviceType,
+ "toAdapter": onuDevice.Type, "onuId": onuDevice.Id,
+ "proxyDeviceId": onuDevice.ProxyAddress.DeviceId})
+ return sendErr
+ }
+ log.Debugw("success sending del tcont to onu adapter", log.Fields{"msg": delTcontMsg})
+ return nil
+}
+
+func (f *OpenOltFlowMgr) deletePendingFlows(Intf uint32, onuID int32, uniID int32) {
+ pnFlDelKey := pendingFlowDeleteKey{Intf, uint32(onuID), uint32(uniID)}
+ if val, ok := f.pendingFlowDelete.Load(pnFlDelKey); ok {
+ if val.(int) > 0 {
+ pnFlDels := val.(int) - 1
+ if pnFlDels > 0 {
+ log.Debugw("flow delete succeeded, more pending",
+ log.Fields{"intf": Intf, "onuID": onuID, "uniID": uniID, "currPendingFlowCnt": pnFlDels})
+ f.pendingFlowDelete.Store(pnFlDelKey, pnFlDels)
+ } else {
+ log.Debugw("all pending flow deletes handled, removing entry from map",
+ log.Fields{"intf": Intf, "onuID": onuID, "uniID": uniID})
+ f.pendingFlowDelete.Delete(pnFlDelKey)
+ }
+ }
+ } else {
+ log.Debugw("no pending delete flows found",
+ log.Fields{"intf": Intf, "onuID": onuID, "uniID": uniID})
+
+ }
+
+}
+
+// Once the gemport is released for a given onu, it also has to be cleared from local cache
+// which was used for deriving the gemport->logicalPortNo during packet-in.
+// Otherwise stale info continues to exist after gemport is freed and wrong logicalPortNo
+// is conveyed to ONOS during packet-in OF message.
+func (f *OpenOltFlowMgr) deleteGemPortFromLocalCache(intfID uint32, onuID uint32, gemPortID uint32) {
+ f.lockCache.Lock()
+ defer f.lockCache.Unlock()
+ onugem := f.onuGemInfo[intfID]
+ for i, onu := range onugem {
+ if onu.OnuID == onuID {
+ for j, gem := range onu.GemPorts {
+ // If the gemport is found, delete it from local cache.
+ if gem == gemPortID {
+ onu.GemPorts = append(onu.GemPorts[:j], onu.GemPorts[j+1:]...)
+ onugem[i] = onu
+ log.Debugw("removed gemport from local cache",
+ log.Fields{"intfID": intfID, "onuID": onuID, "deletedGemPortID": gemPortID, "gemPorts": onu.GemPorts})
+ break
+ }
+ }
+ break
+ }
+ }
+}
+
+//clearResources clears pon resources in kv store and the device
+func (f *OpenOltFlowMgr) clearResources(ctx context.Context, flow *ofp.OfpFlowStats, Intf uint32, onuID int32, uniID int32,
+ gemPortID int32, flowID uint32, flowDirection string,
+ portNum uint32, updatedFlows []rsrcMgr.FlowInfo) error {
+
+ tpID, err := getTpIDFromFlow(flow)
+ if err != nil {
+ log.Error("metadata-is-not-present-invalid-flow-to-process", log.Fields{"pon": Intf, "onuID": onuID, "uniID": uniID})
+ return err
+ }
+
+ if len(updatedFlows) >= 0 {
+ // There are still flows referencing the same flow_id.
+ // So the flow should not be freed yet.
+ // For ex: Case of HSIA where same flow is shared
+ // between DS and US.
+ f.updateFlowInfoToKVStore(ctx, int32(Intf), int32(onuID), int32(uniID), flowID, &updatedFlows)
+ if len(updatedFlows) == 0 {
+ // Do this for subscriber flows only (not trap from NNI flows)
+ if onuID != -1 && uniID != -1 {
+ pnFlDelKey := pendingFlowDeleteKey{Intf, uint32(onuID), uint32(uniID)}
+ if val, ok := f.pendingFlowDelete.Load(pnFlDelKey); !ok {
+ log.Debugw("creating entry for pending flow delete",
+ log.Fields{"intf": Intf, "onuID": onuID, "uniID": uniID})
+ f.pendingFlowDelete.Store(pnFlDelKey, 1)
+ } else {
+ pnFlDels := val.(int) + 1
+ log.Debugw("updating flow delete entry",
+ log.Fields{"intf": Intf, "onuID": onuID, "uniID": uniID, "currPendingFlowCnt": pnFlDels})
+ f.pendingFlowDelete.Store(pnFlDelKey, pnFlDels)
+ }
+
+ defer f.deletePendingFlows(Intf, onuID, uniID)
+ }
+
+ log.Debugw("Releasing flow Id to resource manager", log.Fields{"Intf": Intf, "onuId": onuID, "uniId": uniID, "flowId": flowID})
+ f.resourceMgr.FreeFlowID(ctx, Intf, int32(onuID), int32(uniID), flowID)
+
+ uni := getUniPortPath(Intf, onuID, uniID)
+ tpPath := f.getTPpath(Intf, uni, tpID)
+ log.Debugw("Getting-techprofile-instance-for-subscriber", log.Fields{"TP-PATH": tpPath})
+ techprofileInst, err := f.techprofile[Intf].GetTPInstanceFromKVStore(ctx, tpID, tpPath)
+ if err != nil { // This should not happen, something wrong in KV backend transaction
+ log.Errorw("Error in fetching tech profile instance from KV store", log.Fields{"tpID": 20, "path": tpPath})
+ return err
+ }
+ if techprofileInst == nil {
+ log.Errorw("Tech-profile-instance-does-not-exist-in-KV Store", log.Fields{"tpPath": tpPath})
+ return err
+ }
+
+ gemPK := gemPortKey{Intf, uint32(gemPortID)}
+ if f.isGemPortUsedByAnotherFlow(gemPK) {
+ flowIDs := f.flowsUsedByGemPort[gemPK]
+ for i, flowIDinMap := range flowIDs {
+ if flowIDinMap == flowID {
+ flowIDs = append(flowIDs[:i], flowIDs[i+1:]...)
+ // everytime flowsUsedByGemPort cache is updated the same should be updated
+ // in kv store by calling UpdateFlowIDsForGem
+ f.flowsUsedByGemPort[gemPK] = flowIDs
+ f.resourceMgr.UpdateFlowIDsForGem(ctx, Intf, uint32(gemPortID), flowIDs)
+ break
+ }
+ }
+ log.Debugw("Gem port id is still used by other flows", log.Fields{"gemPortID": gemPortID, "usedByFlows": flowIDs})
+ return nil
+ }
+ log.Debugf("Gem port id %d is not used by another flow - releasing the gem port", gemPortID)
+ f.resourceMgr.RemoveGemPortIDForOnu(ctx, Intf, uint32(onuID), uint32(uniID), uint32(gemPortID))
+ // TODO: The TrafficQueue corresponding to this gem-port also should be removed immediately.
+ // But it is anyway eventually removed later when the TechProfile is freed, so not a big issue for now.
+ f.resourceMgr.RemoveGEMportPonportToOnuMapOnKVStore(ctx, uint32(gemPortID), Intf)
+ f.deleteGemPortFromLocalCache(Intf, uint32(onuID), uint32(gemPortID))
+ f.onuIdsLock.Lock()
+ //everytime an entry is deleted from flowsUsedByGemPort cache, the same should be updated in kv as well
+ // by calling DeleteFlowIDsForGem
+ delete(f.flowsUsedByGemPort, gemPK)
+ f.resourceMgr.DeleteFlowIDsForGem(ctx, Intf, uint32(gemPortID))
+ f.resourceMgr.FreeGemPortID(ctx, Intf, uint32(onuID), uint32(uniID), uint32(gemPortID))
+ f.onuIdsLock.Unlock()
+ // Delete the gem port on the ONU.
+ if err := f.sendDeleteGemPortToChild(Intf, uint32(onuID), uint32(uniID), uint32(gemPortID), tpPath); err != nil {
+ log.Errorw("error processing delete gem-port towards onu",
+ log.Fields{"err": err, "pon": Intf, "onuID": onuID, "uniID": uniID, "gemPortId": gemPortID})
+ }
+
+ ok, _ := f.isTechProfileUsedByAnotherGem(ctx, Intf, uint32(onuID), uint32(uniID), tpID, techprofileInst, uint32(gemPortID))
+ if !ok {
+ f.resourceMgr.RemoveTechProfileIDForOnu(ctx, Intf, uint32(onuID), uint32(uniID), tpID)
+ f.RemoveSchedulerQueues(ctx, schedQueue{direction: tp_pb.Direction_UPSTREAM, intfID: Intf, onuID: uint32(onuID), uniID: uint32(uniID), tpID: tpID, uniPort: portNum, tpInst: techprofileInst})
+ f.RemoveSchedulerQueues(ctx, schedQueue{direction: tp_pb.Direction_DOWNSTREAM, intfID: Intf, onuID: uint32(onuID), uniID: uint32(uniID), tpID: tpID, uniPort: portNum, tpInst: techprofileInst})
+ f.DeleteTechProfileInstance(ctx, Intf, uint32(onuID), uint32(uniID), "", tpID)
+ f.resourceMgr.FreeAllocID(ctx, Intf, uint32(onuID), uint32(uniID), techprofileInst.UsScheduler.AllocID)
+ // Delete the TCONT on the ONU.
+ if err := f.sendDeleteTcontToChild(Intf, uint32(onuID), uint32(uniID), uint32(techprofileInst.UsScheduler.AllocID), tpPath); err != nil {
+ log.Errorw("error processing delete tcont towards onu",
+ log.Fields{"pon": Intf, "onuID": onuID, "uniID": uniID, "allocId": techprofileInst.UsScheduler.AllocID})
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// nolint: gocyclo
+func (f *OpenOltFlowMgr) clearFlowFromResourceManager(ctx context.Context, flow *ofp.OfpFlowStats, flowDirection string) {
+
+ log.Debugw("clearFlowFromResourceManager", log.Fields{"flowDirection": flowDirection, "flow": *flow})
+
+ if flowDirection == Multicast {
+ f.clearMulticastFlowFromResourceManager(ctx, flow)
+ return
+ }
+
+ var updatedFlows []rsrcMgr.FlowInfo
+ classifierInfo := make(map[string]interface{})
+
+ portNum, Intf, onu, uni, inPort, ethType, err := FlowExtractInfo(flow, flowDirection)
+ if err != nil {
+ log.Error(err)
+ return
+ }
+
+ onuID := int32(onu)
+ uniID := int32(uni)
+
+ for _, field := range flows.GetOfbFields(flow) {
+ if field.Type == flows.IP_PROTO {
+ classifierInfo[IPProto] = field.GetIpProto()
+ log.Debug("field-type-ip-proto", log.Fields{"classifierInfo[IP_PROTO]": classifierInfo[IPProto].(uint32)})
+ }
+ }
+ log.Debugw("Extracted access info from flow to be deleted",
+ log.Fields{"ponIntf": Intf, "onuID": onuID, "uniID": uniID})
+
+ if ethType == LldpEthType || ((classifierInfo[IPProto] == IPProtoDhcp) && (flowDirection == "downstream")) {
+ onuID = -1
+ uniID = -1
+ log.Debug("Trap on nni flow set oni, uni to -1")
+ Intf, err = IntfIDFromNniPortNum(inPort)
+ if err != nil {
+ log.Errorw("invalid-in-port-number",
+ log.Fields{
+ "port-number": inPort,
+ "error": err})
+ return
+ }
+ }
+ flowIds := f.resourceMgr.GetCurrentFlowIDsForOnu(ctx, Intf, onuID, uniID)
+ for _, flowID := range flowIds {
+ flowInfo := f.resourceMgr.GetFlowIDInfo(ctx, Intf, onuID, uniID, flowID)
+ if flowInfo == nil {
+ log.Debugw("No FlowInfo found found in KV store",
+ log.Fields{"Intf": Intf, "onuID": onuID, "uniID": uniID, "flowID": flowID})
+ return
+ }
+ updatedFlows = nil
+ for _, flow := range *flowInfo {
+ updatedFlows = append(updatedFlows, flow)
+ }
+
+ for i, storedFlow := range updatedFlows {
+ if flow.Id == storedFlow.LogicalFlowID {
+ removeFlowMessage := openoltpb2.Flow{FlowId: storedFlow.Flow.FlowId, FlowType: storedFlow.Flow.FlowType}
+ log.Debugw("Flow to be deleted", log.Fields{"flow": storedFlow})
+ // DKB
+ if err = f.removeFlowFromDevice(&removeFlowMessage); err != nil {
+ log.Errorw("failed-to-remove-flow", log.Fields{"error": err})
+ return
+ }
+ log.Debug("Flow removed from device successfully")
+ //Remove the Flow from FlowInfo
+ updatedFlows = append(updatedFlows[:i], updatedFlows[i+1:]...)
+ if err = f.clearResources(ctx, flow, Intf, onuID, uniID, storedFlow.Flow.GemportId,
+ flowID, flowDirection, portNum, updatedFlows); err != nil {
+ log.Error("Failed to clear resources for flow", log.Fields{"flow": storedFlow})
+ return
+ }
+ }
+ }
+ }
+}
+
+//clearMulticastFlowFromResourceManager removes a multicast flow from the KV store and
+// clears resources reserved for this multicast flow
+func (f *OpenOltFlowMgr) clearMulticastFlowFromResourceManager(ctx context.Context, flow *ofp.OfpFlowStats) {
+ classifierInfo := make(map[string]interface{})
+ formulateClassifierInfoFromFlow(classifierInfo, flow)
+ inPort, err := f.getInPortOfMulticastFlow(ctx, classifierInfo)
+
+ if err != nil {
+ log.Warnw("No inPort found. Cannot release resources of the multicast flow.", log.Fields{"flowId:": flow.Id})
+ return
+ }
+
+ networkInterfaceID, err := IntfIDFromNniPortNum(inPort)
+ if err != nil {
+ // DKB
+ log.Errorw("invalid-in-port-number",
+ log.Fields{
+ "port-number": inPort,
+ "error": err})
+ return
+ }
+ var onuID = int32(NoneOnuID)
+ var uniID = int32(NoneUniID)
+ var flowID uint32
+ var updatedFlows []rsrcMgr.FlowInfo
+
+ flowIds := f.resourceMgr.GetCurrentFlowIDsForOnu(ctx, networkInterfaceID, onuID, uniID)
+
+ for _, flowID = range flowIds {
+ flowInfo := f.resourceMgr.GetFlowIDInfo(ctx, networkInterfaceID, onuID, uniID, flowID)
+ if flowInfo == nil {
+ log.Debugw("No multicast FlowInfo found in the KV store",
+ log.Fields{"Intf": networkInterfaceID, "onuID": onuID, "uniID": uniID, "flowID": flowID})
+ continue
+ }
+ updatedFlows = nil
+ for _, flow := range *flowInfo {
+ updatedFlows = append(updatedFlows, flow)
+ }
+ for i, storedFlow := range updatedFlows {
+ if flow.Id == storedFlow.LogicalFlowID {
+ removeFlowMessage := openoltpb2.Flow{FlowId: storedFlow.Flow.FlowId, FlowType: storedFlow.Flow.FlowType}
+ log.Debugw("Multicast flow to be deleted", log.Fields{"flow": storedFlow})
+ //remove from device
+ if err := f.removeFlowFromDevice(&removeFlowMessage); err != nil {
+ // DKB
+ log.Errorw("failed-to-remove-multicast-flow",
+ log.Fields{
+ "flow-id": flow.Id,
+ "error": err})
+ return
+ }
+ log.Debugw("Multicast flow removed from device successfully", log.Fields{"flowId": flow.Id})
+ //Remove the Flow from FlowInfo
+ updatedFlows = append(updatedFlows[:i], updatedFlows[i+1:]...)
+ if err := f.updateFlowInfoToKVStore(ctx, int32(networkInterfaceID), NoneOnuID, NoneUniID, flowID, &updatedFlows); err != nil {
+ log.Error("Failed to delete multicast flow from the KV store", log.Fields{"flow": storedFlow, "err": err})
+ return
+ }
+ //release flow id
+ log.Debugw("Releasing multicast flow id", log.Fields{"flowId": flowID, "interfaceID": networkInterfaceID})
+ f.resourceMgr.FreeFlowID(ctx, uint32(networkInterfaceID), NoneOnuID, NoneUniID, flowID)
+ }
+ }
+ }
+}
+
+//RemoveFlow removes the flow from the device
+func (f *OpenOltFlowMgr) RemoveFlow(ctx context.Context, flow *ofp.OfpFlowStats) {
+ log.Debugw("Removing Flow", log.Fields{"flow": flow})
+ var direction string
+ actionInfo := make(map[string]interface{})
+
+ for _, action := range flows.GetActions(flow) {
+ if action.Type == flows.OUTPUT {
+ if out := action.GetOutput(); out != nil {
+ actionInfo[Output] = out.GetPort()
+ log.Debugw("action-type-output", log.Fields{"out_port": actionInfo[Output].(uint32)})
+ } else {
+ log.Error("Invalid output port in action")
+ return
+ }
+ }
+ }
+
+ if flows.HasGroup(flow) {
+ direction = Multicast
+ } else if IsUpstream(actionInfo[Output].(uint32)) {
+ direction = Upstream
+ } else {
+ direction = Downstream
+ }
+ f.clearFlowFromResourceManager(ctx, flow, direction) //TODO: Take care of the limitations
+
+ return
+}
+
+func (f *OpenOltFlowMgr) waitForFlowDeletesToCompleteForOnu(ctx context.Context, intfID uint32, onuID uint32,
+ uniID uint32, ch chan bool) {
+ pnFlDelKey := pendingFlowDeleteKey{intfID, onuID, uniID}
+ for {
+ select {
+ case <-time.After(20 * time.Millisecond):
+ if flowDelRefCnt, ok := f.pendingFlowDelete.Load(pnFlDelKey); !ok || flowDelRefCnt == 0 {
+ log.Debug("pending flow deletes completed")
+ ch <- true
+ return
+ }
+ case <-ctx.Done():
+ log.Error("flow delete wait handler routine canceled")
+ return
+ }
+ }
+}
+
+//isIgmpTrapDownstreamFlow return true if the flow is a downsteam IGMP trap-to-host flow; false otherwise
+func isIgmpTrapDownstreamFlow(classifierInfo map[string]interface{}) bool {
+ if portType := IntfIDToPortTypeName(classifierInfo[InPort].(uint32)); portType == voltha.Port_ETHERNET_NNI {
+ if ethType, ok := classifierInfo[EthType]; ok {
+ if ethType.(uint32) == IPv4EthType {
+ if ipProto, ok := classifierInfo[IPProto]; ok {
+ if ipProto.(uint32) == IgmpProto {
+ return true
+ }
+ }
+ }
+ }
+ }
+ return false
+}
+
+// AddFlow add flow to device
+// nolint: gocyclo
+func (f *OpenOltFlowMgr) AddFlow(ctx context.Context, flow *ofp.OfpFlowStats, flowMetadata *voltha.FlowMetadata) {
+ classifierInfo := make(map[string]interface{})
+ actionInfo := make(map[string]interface{})
+ var UsMeterID uint32
+ var DsMeterID uint32
+
+ log.Debug("Adding Flow", log.Fields{"flow": flow, "flowMetadata": flowMetadata})
+ formulateClassifierInfoFromFlow(classifierInfo, flow)
+
+ err := formulateActionInfoFromFlow(actionInfo, classifierInfo, flow)
+ if err != nil {
+ // Error logging is already done in the called function
+ // So just return in case of error
+ return
+ }
+
+ if flows.HasGroup(flow) {
+ // handle multicast flow
+ f.handleFlowWithGroup(ctx, actionInfo, classifierInfo, flow)
+ return
+ }
+
+ /* Controller bound trap flows */
+ err = formulateControllerBoundTrapFlowInfo(actionInfo, classifierInfo, flow)
+ if err != nil {
+ // error if any, already logged in the called function
+ return
+ }
+
+ log.Infow("Flow ports", log.Fields{"classifierInfo_inport": classifierInfo[InPort], "action_output": actionInfo[Output]})
+ portNo, intfID, onuID, uniID := ExtractAccessFromFlow(classifierInfo[InPort].(uint32), actionInfo[Output].(uint32))
+
+ if ethType, ok := classifierInfo[EthType]; ok {
+ if ethType.(uint32) == LldpEthType {
+ log.Info("Adding LLDP flow")
+ f.addLLDPFlow(ctx, flow, portNo)
+ return
+ }
+ }
+ if ipProto, ok := classifierInfo[IPProto]; ok {
+ if ipProto.(uint32) == IPProtoDhcp {
+ if udpSrc, ok := classifierInfo[UDPSrc]; ok {
+ if udpSrc.(uint32) == uint32(67) || udpSrc.(uint32) == uint32(546) {
+ log.Debug("trap-dhcp-from-nni-flow")
+ f.addDHCPTrapFlowOnNNI(ctx, flow, classifierInfo, portNo)
+ return
+ }
+ }
+ }
+ }
+ if isIgmpTrapDownstreamFlow(classifierInfo) {
+ log.Debug("trap-igmp-from-nni-flow")
+ f.addIgmpTrapFlowOnNNI(ctx, flow, classifierInfo, portNo)
+ return
+ }
+
+ f.deviceHandler.AddUniPortToOnu(intfID, onuID, portNo)
+ f.resourceMgr.AddUniPortToOnuInfo(ctx, intfID, onuID, portNo)
+
+ TpID, err := getTpIDFromFlow(flow)
+ if err != nil {
+ log.Error("metadata-is-not-present-invalid-flow-to-process", log.Fields{"pon": intfID, "onuID": onuID, "uniID": uniID})
+ return
+ }
+ log.Debugw("TPID for this subcriber", log.Fields{"TpId": TpID, "pon": intfID, "onuID": onuID, "uniID": uniID})
+ if IsUpstream(actionInfo[Output].(uint32)) {
+ UsMeterID = flows.GetMeterIdFromFlow(flow)
+ log.Debugw("Upstream-flow-meter-id", log.Fields{"UsMeterID": UsMeterID})
+ } else {
+ DsMeterID = flows.GetMeterIdFromFlow(flow)
+ log.Debugw("Downstream-flow-meter-id", log.Fields{"DsMeterID": DsMeterID})
+
+ }
+
+ pnFlDelKey := pendingFlowDeleteKey{intfID, onuID, uniID}
+ if _, ok := f.pendingFlowDelete.Load(pnFlDelKey); !ok {
+ log.Debugw("no pending flows found, going ahead with flow install", log.Fields{"pon": intfID, "onuID": onuID, "uniID": uniID})
+ f.divideAndAddFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, uint32(TpID), UsMeterID, DsMeterID, flowMetadata)
+ } else {
+ pendingFlowDelComplete := make(chan bool)
+ go f.waitForFlowDeletesToCompleteForOnu(ctx, intfID, onuID, uniID, pendingFlowDelComplete)
+ select {
+ case <-pendingFlowDelComplete:
+ log.Debugw("all pending flow deletes completed", log.Fields{"pon": intfID, "onuID": onuID, "uniID": uniID})
+ f.divideAndAddFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, uint32(TpID), UsMeterID, DsMeterID, flowMetadata)
+
+ case <-time.After(10 * time.Second):
+ log.Errorw("pending flow deletes not completed after timeout", log.Fields{"pon": intfID, "onuID": onuID, "uniID": uniID})
+ }
+ }
+}
+
+// handleFlowWithGroup adds multicast flow to the device.
+func (f *OpenOltFlowMgr) handleFlowWithGroup(ctx context.Context, actionInfo, classifierInfo map[string]interface{}, flow *ofp.OfpFlowStats) error {
+ classifierInfo[PacketTagType] = DoubleTag
+ log.Debugw("add-multicast-flow", log.Fields{"classifierInfo": classifierInfo, "actionInfo": actionInfo})
+
+ inPort, err := f.getInPortOfMulticastFlow(ctx, classifierInfo)
+ if err != nil {
+ return NewErrNotFound("multicast-in-port", log.Fields{"classifier": classifierInfo}, err).Log()
+ }
+ //replace ipDst with ethDst
+ if ipv4Dst, ok := classifierInfo[Ipv4Dst]; ok &&
+ flows.IsMulticastIp(ipv4Dst.(uint32)) {
+ // replace ipv4_dst classifier with eth_dst
+ multicastMac := flows.ConvertToMulticastMacBytes(ipv4Dst.(uint32))
+ delete(classifierInfo, Ipv4Dst)
+ delete(classifierInfo, EthType)
+ classifierInfo[EthDst] = multicastMac
+ log.Debugw("multicast-ip-to-mac-conversion-success", log.Fields{"ip:": ipv4Dst.(uint32), "mac:": multicastMac})
+ }
+
+ onuID := NoneOnuID
+ uniID := NoneUniID
+ gemPortID := NoneGemPortID
+
+ networkInterfaceID, err := IntfIDFromNniPortNum(inPort)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"nni-in-port-number": inPort}, err).Log()
+ }
+
+ flowStoreCookie := getFlowStoreCookie(classifierInfo, uint32(0))
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ log.Debugw("multicast-flow-exists-not-re-adding", log.Fields{"classifierInfo": classifierInfo})
+ return nil
+ }
+ flowID, err := f.resourceMgr.GetFlowID(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), uint32(gemPortID), flowStoreCookie, "", 0, 0)
+ if err != nil {
+ return NewErrNotFound("multicast-flow-id", log.Fields{
+ "interface-id": networkInterfaceID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "gem-port-id": gemPortID,
+ "cookie": flowStoreCookie},
+ err).Log()
+ }
+ classifierProto, err := makeOpenOltClassifierField(classifierInfo)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"classifier": classifierInfo}, err).Log()
+ }
+ groupID := actionInfo[GroupID].(uint32)
+ multicastFlow := openoltpb2.Flow{
+ FlowId: flowID,
+ FlowType: Multicast,
+ NetworkIntfId: int32(networkInterfaceID),
+ GroupId: groupID,
+ Classifier: classifierProto,
+ Priority: int32(flow.Priority),
+ Cookie: flow.Cookie}
+
+ if err = f.addFlowToDevice(ctx, flow, &multicastFlow); err != nil {
+ return NewErrFlowOp("add", flowID, log.Fields{"flow": multicastFlow}, err).Log()
+ }
+ log.Debug("multicast flow added to device successfully")
+ //get cached group
+ group, _, err := f.GetFlowGroupFromKVStore(ctx, groupID, true)
+ if err == nil {
+ //calling groupAdd to set group members after multicast flow creation
+ if f.ModifyGroup(ctx, group) {
+ //cached group can be removed now
+ f.resourceMgr.RemoveFlowGroupFromKVStore(ctx, groupID, true)
+ }
+ }
+
+ flowsToKVStore := f.getUpdatedFlowInfo(ctx, &multicastFlow, flowStoreCookie, MulticastFlow, flowID, flow.Id)
+ if err = f.updateFlowInfoToKVStore(ctx, int32(networkInterfaceID),
+ int32(onuID),
+ int32(uniID),
+ flowID, flowsToKVStore); err != nil {
+ return NewErrPersistence("update", "flow", flowID, log.Fields{"flow": multicastFlow}, err).Log()
+ }
+ return nil
+}
+
+//getInPortOfMulticastFlow return inPort criterion if exists; returns NNI interface of the device otherwise
+func (f *OpenOltFlowMgr) getInPortOfMulticastFlow(ctx context.Context, classifierInfo map[string]interface{}) (uint32, error) {
+ if _, ok := classifierInfo[InPort]; ok {
+ return classifierInfo[InPort].(uint32), nil
+ }
+ // find first NNI port of the device
+ nniPorts, e := f.resourceMgr.GetNNIFromKVStore(ctx)
+ if e == nil && len(nniPorts) > 0 {
+ return nniPorts[0], nil
+ }
+ return 0, NewErrNotFound("nni-port", nil, e).Log()
+}
+
+// AddGroup add or update the group
+func (f *OpenOltFlowMgr) AddGroup(ctx context.Context, group *ofp.OfpGroupEntry) {
+ log.Infow("add-group", log.Fields{"group": group})
+ if group == nil {
+ log.Warn("skipping nil group")
+ return
+ }
+
+ groupToOlt := openoltpb2.Group{
+ GroupId: group.Desc.GroupId,
+ Command: openoltpb2.Group_SET_MEMBERS,
+ Action: f.buildGroupAction(),
+ }
+
+ log.Debugw("Sending group to device", log.Fields{"groupToOlt": groupToOlt})
+ _, err := f.deviceHandler.Client.PerformGroupOperation(ctx, &groupToOlt)
+ if err != nil {
+ log.Errorw("add-group operation failed", log.Fields{"err": err, "groupToOlt": groupToOlt})
+ return
+ }
+ // group members not created yet. So let's store the group
+ if err := f.resourceMgr.AddFlowGroupToKVStore(ctx, group, true); err != nil {
+ log.Errorw("Group cannot be stored in KV store", log.Fields{"groupId": group.Desc.GroupId, "err": err})
+ } else {
+ log.Debugw("add-group operation performed on the device successfully ", log.Fields{"groupToOlt": groupToOlt})
+ }
+}
+
+//buildGroupAction creates and returns a group action
+func (f *OpenOltFlowMgr) buildGroupAction() *openoltpb2.Action {
+ var actionCmd openoltpb2.ActionCmd
+ var action openoltpb2.Action
+ action.Cmd = &actionCmd
+ //pop outer vlan
+ action.Cmd.RemoveOuterTag = true
+ return &action
+}
+
+// ModifyGroup updates the group
+func (f *OpenOltFlowMgr) ModifyGroup(ctx context.Context, group *ofp.OfpGroupEntry) bool {
+ log.Infow("modify-group", log.Fields{"group": group})
+ if group == nil || group.Desc == nil {
+ log.Warn("cannot modify group; group is nil")
+ return false
+ }
+
+ new := f.buildGroup(group.Desc.GroupId, group.Desc.Buckets)
+ //get existing members of the group
+ val, groupExists, err := f.GetFlowGroupFromKVStore(ctx, group.Desc.GroupId, false)
+
+ if err != nil {
+ log.Errorw("Failed to retrieve the group from the store. Cannot modify group.",
+ log.Fields{"groupId": group.Desc.GroupId, "err": err})
+ return false
+ }
+
+ var current *openoltpb2.Group // represents the group on the device
+ if groupExists {
+ // group already exists
+ current = f.buildGroup(group.Desc.GroupId, val.Desc.GetBuckets())
+ log.Debugw("modify-group: group exists.", log.Fields{"group on the device": val, "new": group})
+ } else {
+ current = f.buildGroup(group.Desc.GroupId, nil)
+ }
+
+ log.Debugw("modify-group: comparing current and new.", log.Fields{"group on the device": current, "new": new})
+ // get members to be added
+ membersToBeAdded := f.findDiff(current, new)
+ // get members to be removed
+ membersToBeRemoved := f.findDiff(new, current)
+
+ log.Infow("modify-group -> differences found", log.Fields{"membersToBeAdded": membersToBeAdded,
+ "membersToBeRemoved": membersToBeRemoved, "groupId": group.Desc.GroupId})
+
+ groupToOlt := openoltpb2.Group{
+ GroupId: group.Desc.GroupId,
+ }
+ var added, removed = true, true
+ if membersToBeAdded != nil && len(membersToBeAdded) > 0 {
+ groupToOlt.Command = openoltpb2.Group_ADD_MEMBERS
+ groupToOlt.Members = membersToBeAdded
+ //execute addMembers
+ added = f.callGroupAddRemove(&groupToOlt)
+ }
+ if membersToBeRemoved != nil && len(membersToBeRemoved) > 0 {
+ groupToOlt.Command = openoltpb2.Group_REMOVE_MEMBERS
+ groupToOlt.Members = membersToBeRemoved
+ //execute removeMembers
+ removed = f.callGroupAddRemove(&groupToOlt)
+ }
+
+ //save the modified group
+ if added && removed {
+ if err := f.resourceMgr.AddFlowGroupToKVStore(ctx, group, false); err != nil {
+ log.Errorw("Failed to save the group into kv store", log.Fields{"groupId": group.Desc.GroupId})
+ }
+ log.Debugw("modify-group was success. Storing the group", log.Fields{"group": group, "existingGroup": current})
+ } else {
+ log.Warnw("One of the group add/remove operations has failed. Cannot save group modifications",
+ log.Fields{"group": group})
+ }
+ return added && removed
+}
+
+//callGroupAddRemove performs add/remove buckets operation for the indicated group
+func (f *OpenOltFlowMgr) callGroupAddRemove(group *openoltpb2.Group) bool {
+ if err := f.performGroupOperation(group); err != nil {
+ st, _ := status.FromError(err)
+ //ignore already exists error code
+ if st.Code() != codes.AlreadyExists {
+ return false
+ }
+ }
+ return true
+}
+
+//findDiff compares group members and finds members which only exists in groups2
+func (f *OpenOltFlowMgr) findDiff(group1 *openoltpb2.Group, group2 *openoltpb2.Group) []*openoltpb2.GroupMember {
+ var members []*openoltpb2.GroupMember
+ for _, bucket := range group2.Members {
+ if !f.contains(group1.Members, bucket) {
+ // bucket does not exist and must be added
+ members = append(members, bucket)
+ }
+ }
+ return members
+}
+
+//contains returns true if the members list contains the given member; false otherwise
+func (f *OpenOltFlowMgr) contains(members []*openoltpb2.GroupMember, member *openoltpb2.GroupMember) bool {
+ for _, groupMember := range members {
+ if groupMember.InterfaceId == member.InterfaceId {
+ return true
+ }
+ }
+ return false
+}
+
+//performGroupOperation call performGroupOperation operation of openolt proto
+func (f *OpenOltFlowMgr) performGroupOperation(group *openoltpb2.Group) error {
+ log.Debugw("Sending group to device", log.Fields{"groupToOlt": group, "command": group.Command})
+ _, err := f.deviceHandler.Client.PerformGroupOperation(context.Background(), group)
+ if err != nil {
+ log.Errorw("group operation failed", log.Fields{"err": err, "groupToOlt": group})
+ }
+ return err
+}
+
+//buildGroup build openoltpb2.Group from given group id and bucket list
+func (f *OpenOltFlowMgr) buildGroup(groupID uint32, buckets []*ofp.OfpBucket) *openoltpb2.Group {
+ group := openoltpb2.Group{
+ GroupId: groupID}
+ // create members of the group
+ if buckets != nil {
+ for _, ofBucket := range buckets {
+ member := f.buildMember(ofBucket)
+ if member != nil && !f.contains(group.Members, member) {
+ group.Members = append(group.Members, member)
+ }
+ }
+ }
+ return &group
+}
+
+//buildMember builds openoltpb2.GroupMember from an OpenFlow bucket
+func (f *OpenOltFlowMgr) buildMember(ofBucket *ofp.OfpBucket) *openoltpb2.GroupMember {
+ var outPort uint32
+ outPortFound := false
+ for _, ofAction := range ofBucket.Actions {
+ if ofAction.Type == ofp.OfpActionType_OFPAT_OUTPUT {
+ outPort = ofAction.GetOutput().Port
+ outPortFound = true
+ }
+ }
+
+ if !outPortFound {
+ log.Debugw("bucket skipped since no out port found in it",
+ log.Fields{"ofBucket": ofBucket})
+ return nil
+ }
+ interfaceID := IntfIDFromUniPortNum(outPort)
+ log.Debugw("got associated interface id of the port", log.Fields{"portNumber:": outPort, "interfaceId:": interfaceID})
+ if groupInfo, ok := f.interfaceToMcastQueueMap[interfaceID]; ok {
+ member := openoltpb2.GroupMember{
+ InterfaceId: interfaceID,
+ InterfaceType: openoltpb2.GroupMember_PON,
+ GemPortId: groupInfo.gemPortID,
+ Priority: groupInfo.servicePriority,
+ }
+ //add member to the group
+ return &member
+ }
+ log.Warnf("bucket skipped since interface-2-gem mapping cannot be found",
+ log.Fields{"ofBucket": ofBucket})
+ return nil
+}
+
+//sendTPDownloadMsgToChild send payload
+func (f *OpenOltFlowMgr) sendTPDownloadMsgToChild(intfID uint32, onuID uint32, uniID uint32, uni string, TpID uint32) error {
+
+ onuDevice, err := f.getOnuChildDevice(intfID, onuID)
+ if err != nil {
+ log.Errorw("Error while fetching Child device from core", log.Fields{"onuId": onuID})
+ return err
+ }
+ log.Debugw("Got child device from OLT device handler", log.Fields{"device": *onuDevice})
+
+ tpPath := f.getTPpath(intfID, uni, TpID)
+ tpDownloadMsg := &ic.InterAdapterTechProfileDownloadMessage{UniId: uniID, Path: tpPath}
+ log.Infow("Sending Load-tech-profile-request-to-brcm-onu-adapter", log.Fields{"msg": *tpDownloadMsg})
+ sendErr := f.deviceHandler.AdapterProxy.SendInterAdapterMessage(context.Background(),
+ tpDownloadMsg,
+ ic.InterAdapterMessageType_TECH_PROFILE_DOWNLOAD_REQUEST,
+ f.deviceHandler.deviceType,
+ onuDevice.Type,
+ onuDevice.Id,
+ onuDevice.ProxyAddress.DeviceId, "")
+ if sendErr != nil {
+ log.Errorw("send techprofile-download request error", log.Fields{"fromAdapter": f.deviceHandler.deviceType,
+ "toAdapter": onuDevice.Type, "onuId": onuDevice.Id,
+ "proxyDeviceId": onuDevice.ProxyAddress.DeviceId})
+ return sendErr
+ }
+ log.Debugw("success Sending Load-tech-profile-request-to-brcm-onu-adapter", log.Fields{"msg": tpDownloadMsg})
+ return nil
+}
+
+//UpdateOnuInfo function adds onu info to cache and kvstore
+func (f *OpenOltFlowMgr) UpdateOnuInfo(ctx context.Context, intfID uint32, onuID uint32, serialNum string) {
+
+ f.lockCache.Lock()
+ defer f.lockCache.Unlock()
+ onu := rsrcMgr.OnuGemInfo{OnuID: onuID, SerialNumber: serialNum, IntfID: intfID}
+ f.onuGemInfo[intfID] = append(f.onuGemInfo[intfID], onu)
+ if err := f.resourceMgr.AddOnuGemInfo(ctx, intfID, onu); err != nil {
+ // TODO: VOL-2638
+ log.Errorw("failed to add onu info", log.Fields{"onu": onu})
+ return
+ }
+ log.Debugw("Updated onuinfo", log.Fields{"intfID": intfID, "onuID": onuID, "serialNum": serialNum})
+}
+
+//addGemPortToOnuInfoMap function adds GEMport to ONU map
+func (f *OpenOltFlowMgr) addGemPortToOnuInfoMap(ctx context.Context, intfID uint32, onuID uint32, gemPort uint32) {
+ f.lockCache.Lock()
+ defer f.lockCache.Unlock()
+ onugem := f.onuGemInfo[intfID]
+ // update the gem to the local cache as well as to kv strore
+ for idx, onu := range onugem {
+ if onu.OnuID == onuID {
+ // check if gem already exists , else update the cache and kvstore
+ for _, gem := range onu.GemPorts {
+ if gem == gemPort {
+ log.Debugw("Gem already in cache, no need to update cache and kv store",
+ log.Fields{"gem": gemPort})
+ return
+ }
+ }
+ onugem[idx].GemPorts = append(onugem[idx].GemPorts, gemPort)
+ f.onuGemInfo[intfID] = onugem
+ }
+ }
+ err := f.resourceMgr.AddGemToOnuGemInfo(ctx, intfID, onuID, gemPort)
+ if err != nil {
+ log.Errorw("Failed to add gem to onu", log.Fields{"intfId": intfID, "onuId": onuID, "gemPort": gemPort})
+ return
+ }
+}
+
+// This function Lookup maps by serialNumber or (intfId, gemPort)
+
+//getOnuIDfromGemPortMap Returns OnuID,nil if found or set 0,error if no onuId is found for serialNumber or (intfId, gemPort)
+func (f *OpenOltFlowMgr) getOnuIDfromGemPortMap(serialNumber string, intfID uint32, gemPortID uint32) (uint32, error) {
+
+ f.lockCache.Lock()
+ defer f.lockCache.Unlock()
+
+ log.Debugw("Getting ONU ID from GEM port and PON port", log.Fields{"serialNumber": serialNumber, "intfId": intfID, "gemPortId": gemPortID})
+ // get onuid from the onugem info cache
+ onugem := f.onuGemInfo[intfID]
+ for _, onu := range onugem {
+ for _, gem := range onu.GemPorts {
+ if gem == gemPortID {
+ return onu.OnuID, nil
+ }
+ }
+ }
+ return uint32(0), NewErrNotFound("onu-id", log.Fields{
+ "serial-number": serialNumber,
+ "interface-id": intfID,
+ "gem-port-id": gemPortID},
+ nil).Log()
+}
+
+//GetLogicalPortFromPacketIn function computes logical port UNI/NNI port from packet-in indication and returns the same
+func (f *OpenOltFlowMgr) GetLogicalPortFromPacketIn(ctx context.Context, packetIn *openoltpb2.PacketIndication) (uint32, error) {
+ var logicalPortNum uint32
+ var onuID uint32
+ var err error
+
+ if packetIn.IntfType == "pon" {
+ // packet indication does not have serial number , so sending as nil
+ if onuID, err = f.getOnuIDfromGemPortMap("", packetIn.IntfId, packetIn.GemportId); err != nil {
+ log.Errorw("Unable to get ONU ID from GEM/PON port", log.Fields{"pon port": packetIn.IntfId, "gemport": packetIn.GemportId})
+ return logicalPortNum, err
+ }
+ if packetIn.PortNo != 0 {
+ logicalPortNum = packetIn.PortNo
+ } else {
+ uniID := uint32(0) // FIXME - multi-uni support
+ logicalPortNum = MkUniPortNum(packetIn.IntfId, onuID, uniID)
+ }
+ // Store the gem port through which the packet_in came. Use the same gem port for packet_out
+ f.UpdateGemPortForPktIn(ctx, packetIn.IntfId, onuID, logicalPortNum, packetIn.GemportId)
+ } else if packetIn.IntfType == "nni" {
+ logicalPortNum = IntfIDToPortNo(packetIn.IntfId, voltha.Port_ETHERNET_NNI)
+ }
+ log.Debugw("Retrieved logicalport from packet-in", log.Fields{
+ "logicalPortNum": logicalPortNum,
+ "IntfType": packetIn.IntfType,
+ "packet": hex.EncodeToString(packetIn.Pkt),
+ })
+ return logicalPortNum, nil
+}
+
+//GetPacketOutGemPortID returns gemPortId
+func (f *OpenOltFlowMgr) GetPacketOutGemPortID(ctx context.Context, intfID uint32, onuID uint32, portNum uint32) (uint32, error) {
+ var gemPortID uint32
+ var err error
+
+ f.lockCache.Lock()
+ defer f.lockCache.Unlock()
+ pktInkey := rsrcMgr.PacketInInfoKey{IntfID: intfID, OnuID: onuID, LogicalPort: portNum}
+
+ gemPortID, ok := f.packetInGemPort[pktInkey]
+ if ok {
+ log.Debugw("Found gemport for pktin key", log.Fields{"pktinkey": pktInkey, "gem": gemPortID})
+ return gemPortID, err
+ }
+ //If gem is not found in cache try to get it from kv store, if found in kv store, update the cache and return.
+ gemPortID, err = f.resourceMgr.GetGemPortFromOnuPktIn(ctx, intfID, onuID, portNum)
+ if err == nil {
+ if gemPortID != 0 {
+ f.packetInGemPort[pktInkey] = gemPortID
+ log.Debugw("Found gem port from kv store and updating cache with gemport",
+ log.Fields{"pktinkey": pktInkey, "gem": gemPortID})
+ return gemPortID, nil
+ }
+ }
+ log.Errorw("Failed to get gemport", log.Fields{"pktinkey": pktInkey, "gem": gemPortID})
+ return uint32(0), err
+}
+
+func installFlowOnAllGemports(ctx context.Context,
+ f1 func(ctx context.Context, intfId uint32, onuId uint32, uniId uint32,
+ portNo uint32, classifier map[string]interface{}, action map[string]interface{},
+ logicalFlow *ofp.OfpFlowStats, allocId uint32, gemPortId uint32) error,
+ f2 func(ctx context.Context, intfId uint32, onuId uint32, uniId uint32, portNo uint32,
+ classifier map[string]interface{}, action map[string]interface{},
+ logicalFlow *ofp.OfpFlowStats, allocId uint32, gemPortId uint32, vlanId uint32,
+ ) error,
+ args map[string]uint32,
+ classifier map[string]interface{}, action map[string]interface{},
+ logicalFlow *ofp.OfpFlowStats,
+ gemPorts []uint32,
+ TpInst *tp.TechProfile,
+ FlowType string,
+ vlanID ...uint32) {
+ log.Debugw("Installing flow on all GEM ports", log.Fields{"FlowType": FlowType, "gemPorts": gemPorts, "vlan": vlanID})
+
+ for _, gemPortAttribute := range TpInst.UpstreamGemPortAttributeList {
+ var gemPortID uint32
+ // The bit mapping for a gemport is expressed in tech-profile as a binary string. For example, 0b00000001
+ // We need to trim prefix "0b", before further processing
+ // Once the "0b" prefix is trimmed, we iterate each character in the string to identify which index
+ // in the string is set to binary bit 1 (expressed as char '1' in the binary string).
+ for pos, pbitSet := range strings.TrimPrefix(gemPortAttribute.PbitMap, BinaryStringPrefix) {
+ // If a particular character in the string is set to '1', identify the index of this character from
+ // the LSB position which marks the PCP bit consumed by the given gem port.
+ // This PCP bit now becomes a classifier in the flow.
+ if pbitSet == BinaryBit1 {
+ classifier[VlanPcp] = uint32(len(strings.TrimPrefix(gemPortAttribute.PbitMap, BinaryStringPrefix))) - 1 - uint32(pos)
+ gemPortID = gemPortAttribute.GemportID
+ if FlowType == HsiaFlow || FlowType == DhcpFlow || FlowType == IgmpFlow {
+ f1(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID)
+ } else if FlowType == EapolFlow {
+ f2(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, vlanID[0])
+ } else {
+ log.Errorw("Unrecognized Flow Type", log.Fields{"FlowType": FlowType})
+ return
+ }
+ }
+ }
+ }
+}
+
+func (f *OpenOltFlowMgr) addDHCPTrapFlowOnNNI(ctx context.Context, logicalFlow *ofp.OfpFlowStats, classifier map[string]interface{}, portNo uint32) error {
+ log.Debug("Adding trap-dhcp-of-nni-flow")
+ action := make(map[string]interface{})
+ classifier[PacketTagType] = DoubleTag
+ action[TrapToHost] = true
+ /* We manage flowId resource pool on per PON port basis.
+ Since this situation is tricky, as a hack, we pass the NNI port
+ index (network_intf_id) as PON port Index for the flowId resource
+ pool. Also, there is no ONU Id available for trapping DHCP packets
+ on NNI port, use onu_id as -1 (invalid)
+ ****************** CAVEAT *******************
+ This logic works if the NNI Port Id falls within the same valid
+ range of PON Port Ids. If this doesn't work for some OLT Vendor
+ we need to have a re-look at this.
+ *********************************************
+ */
+ onuID := -1
+ uniID := -1
+ gemPortID := -1
+ allocID := -1
+ networkInterfaceID, err := getNniIntfID(classifier, action)
+ if err != nil {
+ return NewErrNotFound("nni-intreface-id", log.Fields{
+ "classifier": classifier,
+ "action": action},
+ err).Log()
+ }
+
+ flowStoreCookie := getFlowStoreCookie(classifier, uint32(0))
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ log.Debug("Flow-exists-not-re-adding")
+ return nil
+ }
+ flowID, err := f.resourceMgr.GetFlowID(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), uint32(gemPortID), flowStoreCookie, "", 0)
+ if err != nil {
+ return NewErrNotFound("dhcp-trap-nni-flow-id", log.Fields{
+ "interface-id": networkInterfaceID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "gem-port-id": gemPortID,
+ "cookie": flowStoreCookie},
+ err).Log()
+ }
+ classifierProto, err := makeOpenOltClassifierField(classifier)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"classifier": classifier}, err).Log()
+ }
+ log.Debugw("Created classifier proto", log.Fields{"classifier": *classifierProto})
+ actionProto, err := makeOpenOltActionField(action)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"action": action}, err).Log()
+ }
+ log.Debugw("Created action proto", log.Fields{"action": *actionProto})
+ downstreamflow := openoltpb2.Flow{AccessIntfId: int32(-1), // AccessIntfId not required
+ OnuId: int32(onuID), // OnuId not required
+ UniId: int32(uniID), // UniId not used
+ FlowId: flowID,
+ FlowType: Downstream,
+ AllocId: int32(allocID), // AllocId not used
+ NetworkIntfId: int32(networkInterfaceID),
+ GemportId: int32(gemPortID), // GemportId not used
+ Classifier: classifierProto,
+ Action: actionProto,
+ Priority: int32(logicalFlow.Priority),
+ Cookie: logicalFlow.Cookie,
+ PortNo: portNo}
+ if err := f.addFlowToDevice(ctx, logicalFlow, &downstreamflow); err != nil {
+ return NewErrFlowOp("add", flowID, log.Fields{"flow": downstreamflow}, err).Log()
+ }
+ log.Debug("DHCP trap on NNI flow added to device successfully")
+ flowsToKVStore := f.getUpdatedFlowInfo(ctx, &downstreamflow, flowStoreCookie, "", flowID, logicalFlow.Id)
+ if err := f.updateFlowInfoToKVStore(ctx, int32(networkInterfaceID),
+ int32(onuID),
+ int32(uniID),
+ flowID, flowsToKVStore); err != nil {
+ return NewErrPersistence("update", "flow", flowID, log.Fields{"flow": downstreamflow}, err).Log()
+ }
+ return nil
+}
+
+//getPacketTypeFromClassifiers finds and returns packet type of a flow by checking flow classifiers
+func getPacketTypeFromClassifiers(classifierInfo map[string]interface{}) string {
+ var packetType string
+ ovid, ivid := false, false
+ if vlanID, ok := classifierInfo[VlanVid].(uint32); ok {
+ vid := vlanID & VlanvIDMask
+ if vid != ReservedVlan {
+ ovid = true
+ }
+ }
+ if metadata, ok := classifierInfo[Metadata].(uint64); ok {
+ vid := uint32(metadata)
+ if vid != ReservedVlan {
+ ivid = true
+ }
+ }
+ if ovid && ivid {
+ packetType = DoubleTag
+ } else if !ovid && !ivid {
+ packetType = Untagged
+ } else {
+ packetType = SingleTag
+ }
+ return packetType
+}
+
+//addIgmpTrapFlowOnNNI adds a trap-to-host flow on NNI
+func (f *OpenOltFlowMgr) addIgmpTrapFlowOnNNI(ctx context.Context, logicalFlow *ofp.OfpFlowStats, classifier map[string]interface{}, portNo uint32) error {
+ log.Debugw("Adding igmp-trap-of-nni-flow", log.Fields{"classifierInfo": classifier})
+ action := make(map[string]interface{})
+ classifier[PacketTagType] = getPacketTypeFromClassifiers(classifier)
+ action[TrapToHost] = true
+ /* We manage flowId resource pool on per PON port basis.
+ Since this situation is tricky, as a hack, we pass the NNI port
+ index (network_intf_id) as PON port Index for the flowId resource
+ pool. Also, there is no ONU Id available for trapping packets
+ on NNI port, use onu_id as -1 (invalid)
+ ****************** CAVEAT *******************
+ This logic works if the NNI Port Id falls within the same valid
+ range of PON Port Ids. If this doesn't work for some OLT Vendor
+ we need to have a re-look at this.
+ *********************************************
+ */
+ onuID := -1
+ uniID := -1
+ gemPortID := -1
+ allocID := -1
+ networkInterfaceID, err := getNniIntfID(classifier, action)
+ if err != nil {
+ return NewErrNotFound("nni-interface-id", log.Fields{
+ "classifier": classifier,
+ "action": action},
+ err).Log()
+ }
+ flowStoreCookie := getFlowStoreCookie(classifier, uint32(0))
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ log.Debug("igmp-flow-exists-not-re-adding")
+ return nil
+ }
+ flowID, err := f.resourceMgr.GetFlowID(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), uint32(gemPortID), flowStoreCookie, "", 0, 0)
+ if err != nil {
+ return NewErrNotFound("igmp-flow-id", log.Fields{
+ "interface-id": networkInterfaceID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "gem-port-id": gemPortID,
+ "cookie": flowStoreCookie},
+ err).Log()
+ }
+ classifierProto, err := makeOpenOltClassifierField(classifier)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"classifier": classifier}, err).Log()
+ }
+ log.Debugw("Created classifier proto for the IGMP flow", log.Fields{"classifier": *classifierProto})
+ actionProto, err := makeOpenOltActionField(action)
+ if err != nil {
+ return NewErrInvalidValue(log.Fields{"action": action}, err).Log()
+ }
+ log.Debugw("Created action proto for the IGMP flow", log.Fields{"action": *actionProto})
+ downstreamflow := openoltpb2.Flow{AccessIntfId: int32(-1), // AccessIntfId not required
+ OnuId: int32(onuID), // OnuId not required
+ UniId: int32(uniID), // UniId not used
+ FlowId: flowID,
+ FlowType: Downstream,
+ AllocId: int32(allocID), // AllocId not used
+ NetworkIntfId: int32(networkInterfaceID),
+ GemportId: int32(gemPortID), // GemportId not used
+ Classifier: classifierProto,
+ Action: actionProto,
+ Priority: int32(logicalFlow.Priority),
+ Cookie: logicalFlow.Cookie,
+ PortNo: portNo}
+ if err := f.addFlowToDevice(ctx, logicalFlow, &downstreamflow); err != nil {
+ return NewErrFlowOp("add", flowID, log.Fields{"flow": downstreamflow}, err).Log()
+ }
+ log.Debug("IGMP Trap on NNI flow added to device successfully")
+ flowsToKVStore := f.getUpdatedFlowInfo(ctx, &downstreamflow, flowStoreCookie, "", flowID, logicalFlow.Id)
+ if err := f.updateFlowInfoToKVStore(ctx, int32(networkInterfaceID),
+ int32(onuID),
+ int32(uniID),
+ flowID, flowsToKVStore); err != nil {
+ return NewErrPersistence("update", "flow", flowID, log.Fields{"flow": downstreamflow}, err).Log()
+ }
+ return nil
+}
+
+func verifyMeterIDAndGetDirection(MeterID uint32, Dir tp_pb.Direction) (string, error) {
+ if MeterID == 0 { // This should never happen
+ return "", NewErrInvalidValue(log.Fields{"meter-id": MeterID}, nil).Log()
+ }
+ if Dir == tp_pb.Direction_UPSTREAM {
+ return "upstream", nil
+ } else if Dir == tp_pb.Direction_DOWNSTREAM {
+ return "downstream", nil
+ }
+ return "", nil
+}
+
+func (f *OpenOltFlowMgr) checkAndAddFlow(ctx context.Context, args map[string]uint32, classifierInfo map[string]interface{},
+ actionInfo map[string]interface{}, flow *ofp.OfpFlowStats, TpInst *tp.TechProfile, gemPorts []uint32,
+ TpID uint32, uni string) {
+ var gemPort uint32
+ intfID := args[IntfID]
+ onuID := args[OnuID]
+ uniID := args[UniID]
+ portNo := args[PortNo]
+ allocID := TpInst.UsScheduler.AllocID
+ if ipProto, ok := classifierInfo[IPProto]; ok {
+ if ipProto.(uint32) == IPProtoDhcp {
+ log.Info("Adding DHCP flow")
+ if pcp, ok := classifierInfo[VlanPcp]; ok {
+ gemPort = f.techprofile[intfID].GetGemportIDForPbit(TpInst,
+ tp_pb.Direction_UPSTREAM,
+ pcp.(uint32))
+ //Adding DHCP upstream flow
+ f.addDHCPTrapFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort)
+ } else {
+ //Adding DHCP upstream flow to all gemports
+ installFlowOnAllGemports(ctx, f.addDHCPTrapFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, DhcpFlow)
+ }
+
+ } else if ipProto == IgmpProto {
+ log.Infow("Adding Us IGMP flow", log.Fields{"intfID": intfID, "onuID": onuID, "uniID": uniID, "classifierInfo:": classifierInfo})
+ if pcp, ok := classifierInfo[VlanPcp]; ok {
+ gemPort = f.techprofile[intfID].GetGemportIDForPbit(TpInst,
+ tp_pb.Direction_UPSTREAM,
+ pcp.(uint32))
+ f.addIGMPTrapFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort)
+ } else {
+ //Adding IGMP upstream flow to all gem ports
+ installFlowOnAllGemports(ctx, f.addIGMPTrapFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, IgmpFlow)
+ }
+ } else {
+ log.Errorw("Invalid-Classifier-to-handle", log.Fields{"classifier": classifierInfo, "action": actionInfo})
+ return
+ }
+ } else if ethType, ok := classifierInfo[EthType]; ok {
+ if ethType.(uint32) == EapEthType {
+ log.Info("Adding EAPOL flow")
+ var vlanID uint32
+ if val, ok := classifierInfo[VlanVid]; ok {
+ vlanID = (val.(uint32)) & VlanvIDMask
+ } else {
+ vlanID = DefaultMgmtVlan
+ }
+ if pcp, ok := classifierInfo[VlanPcp]; ok {
+ gemPort = f.techprofile[intfID].GetGemportIDForPbit(TpInst,
+ tp_pb.Direction_UPSTREAM,
+ pcp.(uint32))
+
+ f.addEAPOLFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort, vlanID)
+ } else {
+ installFlowOnAllGemports(ctx, nil, f.addEAPOLFlow, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, EapolFlow, vlanID)
+ }
+ }
+ } else if _, ok := actionInfo[PushVlan]; ok {
+ log.Info("Adding upstream data rule")
+ if pcp, ok := classifierInfo[VlanPcp]; ok {
+ gemPort = f.techprofile[intfID].GetGemportIDForPbit(TpInst,
+ tp_pb.Direction_UPSTREAM,
+ pcp.(uint32))
+ //Adding HSIA upstream flow
+ f.addUpstreamDataFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort)
+ } else {
+ //Adding HSIA upstream flow to all gemports
+ installFlowOnAllGemports(ctx, f.addUpstreamDataFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, HsiaFlow)
+ }
+ } else if _, ok := actionInfo[PopVlan]; ok {
+ log.Info("Adding Downstream data rule")
+ if pcp, ok := classifierInfo[VlanPcp]; ok {
+ gemPort = f.techprofile[intfID].GetGemportIDForPbit(TpInst,
+ tp_pb.Direction_DOWNSTREAM,
+ pcp.(uint32))
+ //Adding HSIA downstream flow
+ f.addDownstreamDataFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort)
+ } else {
+ //Adding HSIA downstream flow to all gemports
+ installFlowOnAllGemports(ctx, f.addDownstreamDataFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, HsiaFlow)
+ }
+ } else {
+ log.Errorw("Invalid-flow-type-to-handle", log.Fields{"classifier": classifierInfo, "action": actionInfo, "flow": flow})
+ return
+ }
+ // Send Techprofile download event to child device in go routine as it takes time
+ go f.sendTPDownloadMsgToChild(intfID, onuID, uniID, uni, TpID)
+}
+
+func (f *OpenOltFlowMgr) isGemPortUsedByAnotherFlow(gemPK gemPortKey) bool {
+ flowIDList := f.flowsUsedByGemPort[gemPK]
+ if len(flowIDList) > 1 {
+ return true
+ }
+ return false
+}
+
+func (f *OpenOltFlowMgr) isTechProfileUsedByAnotherGem(ctx context.Context, ponIntf uint32, onuID uint32, uniID uint32, tpID uint32, tpInst *tp.TechProfile, gemPortID uint32) (bool, uint32) {
+ currentGemPorts := f.resourceMgr.GetCurrentGEMPortIDsForOnu(ctx, ponIntf, onuID, uniID)
+ tpGemPorts := tpInst.UpstreamGemPortAttributeList
+ for _, currentGemPort := range currentGemPorts {
+ for _, tpGemPort := range tpGemPorts {
+ if (currentGemPort == tpGemPort.GemportID) && (currentGemPort != gemPortID) {
+ return true, currentGemPort
+ }
+ }
+ }
+ if tpInst.InstanceCtrl.Onu == "single-instance" {
+ // The TP information for the given TP ID, PON ID, ONU ID, UNI ID should be removed.
+ f.resourceMgr.RemoveTechProfileIDForOnu(ctx, ponIntf, uint32(onuID), uint32(uniID), tpID)
+ f.DeleteTechProfileInstance(ctx, ponIntf, uint32(onuID), uint32(uniID), "", tpID)
+
+ // Although we cleaned up TP Instance for the given (PON ID, ONU ID, UNI ID), the TP might
+ // still be used on other uni ports.
+ // So, we need to check and make sure that no other gem port is referring to the given TP ID
+ // on any other uni port.
+ tpInstances := f.techprofile[ponIntf].FindAllTpInstances(ctx, tpID, ponIntf, onuID)
+ log.Debugw("got single instance tp instances", log.Fields{"tpInstances": tpInstances})
+ for i := 0; i < len(tpInstances); i++ {
+ tpI := tpInstances[i]
+ tpGemPorts := tpI.UpstreamGemPortAttributeList
+ for _, tpGemPort := range tpGemPorts {
+ if tpGemPort.GemportID != gemPortID {
+ log.Debugw("single instance tp is in use by gem", log.Fields{"gemPort": tpGemPort.GemportID})
+ return true, tpGemPort.GemportID
+ }
+ }
+ }
+ }
+ log.Debug("tech profile is not in use by any gem")
+ return false, 0
+}
+
+func formulateClassifierInfoFromFlow(classifierInfo map[string]interface{}, flow *ofp.OfpFlowStats) {
+ for _, field := range flows.GetOfbFields(flow) {
+ if field.Type == flows.ETH_TYPE {
+ classifierInfo[EthType] = field.GetEthType()
+ log.Debug("field-type-eth-type", log.Fields{"classifierInfo[ETH_TYPE]": classifierInfo[EthType].(uint32)})
+ } else if field.Type == flows.ETH_DST {
+ classifierInfo[EthDst] = field.GetEthDst()
+ log.Debug("field-type-eth-type", log.Fields{"classifierInfo[ETH_DST]": classifierInfo[EthDst].([]uint8)})
+ } else if field.Type == flows.IP_PROTO {
+ classifierInfo[IPProto] = field.GetIpProto()
+ log.Debug("field-type-ip-proto", log.Fields{"classifierInfo[IP_PROTO]": classifierInfo[IPProto].(uint32)})
+ } else if field.Type == flows.IN_PORT {
+ classifierInfo[InPort] = field.GetPort()
+ log.Debug("field-type-in-port", log.Fields{"classifierInfo[IN_PORT]": classifierInfo[InPort].(uint32)})
+ } else if field.Type == flows.VLAN_VID {
+ classifierInfo[VlanVid] = field.GetVlanVid() & 0xfff
+ log.Debug("field-type-vlan-vid", log.Fields{"classifierInfo[VLAN_VID]": classifierInfo[VlanVid].(uint32)})
+ } else if field.Type == flows.VLAN_PCP {
+ classifierInfo[VlanPcp] = field.GetVlanPcp()
+ log.Debug("field-type-vlan-pcp", log.Fields{"classifierInfo[VLAN_PCP]": classifierInfo[VlanPcp].(uint32)})
+ } else if field.Type == flows.UDP_DST {
+ classifierInfo[UDPDst] = field.GetUdpDst()
+ log.Debug("field-type-udp-dst", log.Fields{"classifierInfo[UDP_DST]": classifierInfo[UDPDst].(uint32)})
+ } else if field.Type == flows.UDP_SRC {
+ classifierInfo[UDPSrc] = field.GetUdpSrc()
+ log.Debug("field-type-udp-src", log.Fields{"classifierInfo[UDP_SRC]": classifierInfo[UDPSrc].(uint32)})
+ } else if field.Type == flows.IPV4_DST {
+ classifierInfo[Ipv4Dst] = field.GetIpv4Dst()
+ log.Debug("field-type-ipv4-dst", log.Fields{"classifierInfo[IPV4_DST]": classifierInfo[Ipv4Dst].(uint32)})
+ } else if field.Type == flows.IPV4_SRC {
+ classifierInfo[Ipv4Src] = field.GetIpv4Src()
+ log.Debug("field-type-ipv4-src", log.Fields{"classifierInfo[IPV4_SRC]": classifierInfo[Ipv4Src].(uint32)})
+ } else if field.Type == flows.METADATA {
+ classifierInfo[Metadata] = field.GetTableMetadata()
+ log.Debug("field-type-metadata", log.Fields{"classifierInfo[Metadata]": classifierInfo[Metadata].(uint64)})
+ } else if field.Type == flows.TUNNEL_ID {
+ classifierInfo[TunnelID] = field.GetTunnelId()
+ log.Debug("field-type-tunnelId", log.Fields{"classifierInfo[TUNNEL_ID]": classifierInfo[TunnelID].(uint64)})
+ } else {
+ log.Errorw("Un supported field type", log.Fields{"type": field.Type})
+ return
+ }
+ }
+}
+
+func formulateActionInfoFromFlow(actionInfo, classifierInfo map[string]interface{}, flow *ofp.OfpFlowStats) error {
+ for _, action := range flows.GetActions(flow) {
+ if action.Type == flows.OUTPUT {
+ if out := action.GetOutput(); out != nil {
+ actionInfo[Output] = out.GetPort()
+ log.Debugw("action-type-output", log.Fields{"out_port": actionInfo[Output].(uint32)})
+ } else {
+ return NewErrInvalidValue(log.Fields{"output-port": nil}, nil).Log()
+ }
+ } else if action.Type == flows.POP_VLAN {
+ actionInfo[PopVlan] = true
+ log.Debugw("action-type-pop-vlan", log.Fields{"in_port": classifierInfo[InPort].(uint32)})
+ } else if action.Type == flows.PUSH_VLAN {
+ if out := action.GetPush(); out != nil {
+ if tpid := out.GetEthertype(); tpid != 0x8100 {
+ log.Errorw("Invalid ethertype in push action", log.Fields{"ethertype": actionInfo[PushVlan].(int32)})
+ } else {
+ actionInfo[PushVlan] = true
+ actionInfo[TPID] = tpid
+ log.Debugw("action-type-push-vlan",
+ log.Fields{"push_tpid": actionInfo[TPID].(uint32), "in_port": classifierInfo[InPort].(uint32)})
+ }
+ }
+ } else if action.Type == flows.SET_FIELD {
+ if out := action.GetSetField(); out != nil {
+ if field := out.GetField(); field != nil {
+ if ofClass := field.GetOxmClass(); ofClass != ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC {
+ return NewErrInvalidValue(log.Fields{"openflow-class": ofClass}, nil).Log()
+ }
+ /*log.Debugw("action-type-set-field",log.Fields{"field": field, "in_port": classifierInfo[IN_PORT].(uint32)})*/
+ formulateSetFieldActionInfoFromFlow(field, actionInfo)
+ }
+ }
+ } else if action.Type == flows.GROUP {
+ formulateGroupActionInfoFromFlow(action, actionInfo)
+ } else {
+ return NewErrInvalidValue(log.Fields{"action-type": action.Type}, nil).Log()
+ }
+ }
+ return nil
+}
+
+func formulateSetFieldActionInfoFromFlow(field *ofp.OfpOxmField, actionInfo map[string]interface{}) {
+ if ofbField := field.GetOfbField(); ofbField != nil {
+ if fieldtype := ofbField.GetType(); fieldtype == ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID {
+ if vlan := ofbField.GetVlanVid(); vlan != 0 {
+ actionInfo[VlanVid] = vlan & 0xfff
+ log.Debugw("action-set-vlan-vid", log.Fields{"actionInfo[VLAN_VID]": actionInfo[VlanVid].(uint32)})
+ } else {
+ log.Error("No Invalid vlan id in set vlan-vid action")
+ }
+ } else {
+ log.Errorw("unsupported-action-set-field-type", log.Fields{"type": fieldtype})
+ }
+ }
+}
+
+func formulateGroupActionInfoFromFlow(action *ofp.OfpAction, actionInfo map[string]interface{}) {
+ if action.GetGroup() == nil {
+ log.Warn("No group entry found in the group action")
+ } else {
+ actionInfo[GroupID] = action.GetGroup().GroupId
+ log.Debugw("action-group-id", log.Fields{"actionInfo[GroupID]": actionInfo[GroupID].(uint32)})
+ }
+}
+
+func formulateControllerBoundTrapFlowInfo(actionInfo, classifierInfo map[string]interface{}, flow *ofp.OfpFlowStats) error {
+ if isControllerFlow := IsControllerBoundFlow(actionInfo[Output].(uint32)); isControllerFlow {
+ log.Debug("Controller bound trap flows, getting inport from tunnelid")
+ /* Get UNI port/ IN Port from tunnel ID field for upstream controller bound flows */
+ if portType := IntfIDToPortTypeName(classifierInfo[InPort].(uint32)); portType == voltha.Port_PON_OLT {
+ if uniPort := flows.GetChildPortFromTunnelId(flow); uniPort != 0 {
+ classifierInfo[InPort] = uniPort
+ log.Debugw("upstream pon-to-controller-flow,inport-in-tunnelid", log.Fields{"newInPort": classifierInfo[InPort].(uint32), "outPort": actionInfo[Output].(uint32)})
+ } else {
+ return NewErrNotFound("child-in-port", log.Fields{
+ "reason": "upstream pon-to-controller-flow, NO-inport-in-tunnelid",
+ "flow": flow}, nil).Log()
+ }
+ }
+ } else {
+ log.Debug("Non-Controller flows, getting uniport from tunnelid")
+ // Downstream flow from NNI to PON port , Use tunnel ID as new OUT port / UNI port
+ if portType := IntfIDToPortTypeName(actionInfo[Output].(uint32)); portType == voltha.Port_PON_OLT {
+ if uniPort := flows.GetChildPortFromTunnelId(flow); uniPort != 0 {
+ actionInfo[Output] = uniPort
+ log.Debugw("downstream-nni-to-pon-port-flow, outport-in-tunnelid", log.Fields{"newOutPort": actionInfo[Output].(uint32), "outPort": actionInfo[Output].(uint32)})
+ } else {
+ return NewErrNotFound("out-port", log.Fields{
+ "reason": "downstream-nni-to-pon-port-flow, no-outport-in-tunnelid",
+ "flow": flow}, nil).Log()
+ }
+ // Upstream flow from PON to NNI port , Use tunnel ID as new IN port / UNI port
+ } else if portType := IntfIDToPortTypeName(classifierInfo[InPort].(uint32)); portType == voltha.Port_PON_OLT {
+ if uniPort := flows.GetChildPortFromTunnelId(flow); uniPort != 0 {
+ classifierInfo[InPort] = uniPort
+ log.Debugw("upstream-pon-to-nni-port-flow, inport-in-tunnelid", log.Fields{"newInPort": actionInfo[Output].(uint32),
+ "outport": actionInfo[Output].(uint32)})
+ } else {
+ return NewErrNotFound("nni-port", log.Fields{
+ "reason": "upstream-pon-to-nni-port-flow, no-inport-in-tunnelid",
+ "in-port": classifierInfo[InPort].(uint32),
+ "out-port": actionInfo[Output].(uint32),
+ "flow": flow}, nil).Log()
+ }
+ }
+ }
+ return nil
+}
+
+func getTpIDFromFlow(flow *ofp.OfpFlowStats) (uint32, error) {
+ /* 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 := flows.GetMetadataFromWriteMetadataAction(flow)
+ if metadata == 0 {
+ return 0, NewErrNotFound("metadata", log.Fields{"flow": flow}, nil).Log()
+ }
+ TpID := flows.GetTechProfileIDFromWriteMetaData(metadata)
+ return uint32(TpID), nil
+}
+
+func appendUnique(slice []uint32, item uint32) []uint32 {
+ for _, sliceElement := range slice {
+ if sliceElement == item {
+ return slice
+ }
+ }
+ return append(slice, item)
+}
+
+// getNniIntfID gets nni intf id from the flow classifier/action
+func getNniIntfID(classifier map[string]interface{}, action map[string]interface{}) (uint32, error) {
+
+ portType := IntfIDToPortTypeName(classifier[InPort].(uint32))
+ if portType == voltha.Port_PON_OLT {
+ intfID, err := IntfIDFromNniPortNum(action[Output].(uint32))
+ if err != nil {
+ log.Debugw("invalid-action-port-number",
+ log.Fields{
+ "port-number": action[Output].(uint32),
+ "error": err})
+ return uint32(0), err
+ }
+ log.Debugw("output Nni IntfID is", log.Fields{"intfid": intfID})
+ return intfID, nil
+ } else if portType == voltha.Port_ETHERNET_NNI {
+ intfID, err := IntfIDFromNniPortNum(classifier[InPort].(uint32))
+ if err != nil {
+ log.Debugw("invalid-classifier-port-number",
+ log.Fields{
+ "port-number": action[Output].(uint32),
+ "error": err})
+ return uint32(0), err
+ }
+ log.Debugw("input Nni IntfID is", log.Fields{"intfid": intfID})
+ return intfID, nil
+ }
+ return uint32(0), nil
+}
+
+// UpdateGemPortForPktIn updates gemport for packet-in in to the cache and to the kv store as well.
+func (f *OpenOltFlowMgr) UpdateGemPortForPktIn(ctx context.Context, intfID uint32, onuID uint32, logicalPort uint32, gemPort uint32) {
+ pktInkey := rsrcMgr.PacketInInfoKey{IntfID: intfID, OnuID: onuID, LogicalPort: logicalPort}
+
+ f.lockCache.Lock()
+ defer f.lockCache.Unlock()
+ lookupGemPort, ok := f.packetInGemPort[pktInkey]
+ if ok {
+ if lookupGemPort == gemPort {
+ log.Debugw("pktin key/value found in cache , no need to update kv as we are assuming both will be in sync",
+ log.Fields{"pktinkey": pktInkey, "gem": gemPort})
+ return
+ }
+ }
+ f.packetInGemPort[pktInkey] = gemPort
+
+ f.resourceMgr.UpdateGemPortForPktIn(ctx, pktInkey, gemPort)
+ log.Debugw("pktin key not found in local cache or value is different. updating cache and kv store", log.Fields{"pktinkey": pktInkey, "gem": gemPort})
+ return
+}
+
+// AddUniPortToOnuInfo adds uni port to the onugem info both in cache and kvstore.
+func (f *OpenOltFlowMgr) AddUniPortToOnuInfo(ctx context.Context, intfID uint32, onuID uint32, portNum uint32) {
+
+ f.lockCache.Lock()
+ defer f.lockCache.Unlock()
+ onugem := f.onuGemInfo[intfID]
+ for idx, onu := range onugem {
+ if onu.OnuID == onuID {
+ for _, uni := range onu.UniPorts {
+ if uni == portNum {
+ log.Debugw("uni already in cache, no need to update cache and kv store",
+ log.Fields{"uni": portNum})
+ return
+ }
+ }
+ onugem[idx].UniPorts = append(onugem[idx].UniPorts, portNum)
+ f.onuGemInfo[intfID] = onugem
+ }
+ }
+ f.resourceMgr.AddUniPortToOnuInfo(ctx, intfID, onuID, portNum)
+}
+
+func (f *OpenOltFlowMgr) loadFlowIDlistForGem(ctx context.Context, intf uint32) {
+ flowIDsList, err := f.resourceMgr.GetFlowIDsGemMapForInterface(ctx, intf)
+ if err != nil {
+ log.Error("Failed to get flowid list per gem", log.Fields{"intf": intf})
+ return
+ }
+ for gem, FlowIDs := range flowIDsList {
+ gemPK := gemPortKey{intf, uint32(gem)}
+ f.flowsUsedByGemPort[gemPK] = FlowIDs
+ }
+ return
+}
+
+//loadInterfaceToMulticastQueueMap reads multicast queues per interface from the KV store
+//and put them into interfaceToMcastQueueMap.
+func (f *OpenOltFlowMgr) loadInterfaceToMulticastQueueMap(ctx context.Context) {
+ storedMulticastQueueMap, err := f.resourceMgr.GetMcastQueuePerInterfaceMap(ctx)
+ if err != nil {
+ log.Error("Failed to get pon interface to multicast queue map")
+ return
+ }
+ for intf, queueInfo := range storedMulticastQueueMap {
+ q := queueInfoBrief{
+ gemPortID: queueInfo[0],
+ servicePriority: queueInfo[1],
+ }
+ f.interfaceToMcastQueueMap[intf] = &q
+ }
+}
+
+//GetFlowGroupFromKVStore fetches and returns flow group from the KV store. Returns (nil, false, error) if any problem occurs during
+//fetching the data. Returns (group, true, nil) if the group is fetched and returned successfully.
+//Returns (nil, false, nil) if the group does not exists in the KV store.
+func (f *OpenOltFlowMgr) GetFlowGroupFromKVStore(ctx context.Context, groupID uint32, cached bool) (*ofp.OfpGroupEntry, bool, error) {
+ exists, groupInfo, err := f.resourceMgr.GetFlowGroupFromKVStore(ctx, groupID, cached)
+ if err != nil {
+ return nil, false, NewErrNotFound("flow-group", log.Fields{"group-id": groupID}, err).Log()
+ }
+ if exists {
+ return newGroup(groupInfo.GroupID, groupInfo.OutPorts), exists, nil
+ }
+ return nil, exists, nil
+}
+
+func newGroup(groupID uint32, outPorts []uint32) *ofp.OfpGroupEntry {
+ groupDesc := ofp.OfpGroupDesc{
+ Type: ofp.OfpGroupType_OFPGT_ALL,
+ GroupId: groupID,
+ }
+ groupEntry := ofp.OfpGroupEntry{
+ Desc: &groupDesc,
+ }
+ for i := 0; i < len(outPorts); i++ {
+ var acts []*ofp.OfpAction
+ acts = append(acts, flows.Output(outPorts[i]))
+ bucket := ofp.OfpBucket{
+ Actions: acts,
+ }
+ groupDesc.Buckets = append(groupDesc.Buckets, &bucket)
+ }
+ return &groupEntry
+}
diff --git a/internal/pkg/core/openolt_flowmgr_test.go b/internal/pkg/core/openolt_flowmgr_test.go
new file mode 100644
index 0000000..2890b19
--- /dev/null
+++ b/internal/pkg/core/openolt_flowmgr_test.go
@@ -0,0 +1,996 @@
+/*
+ * 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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "context"
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/db"
+ fu "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ tp "github.com/opencord/voltha-lib-go/v3/pkg/techprofile"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/resourcemanager"
+ rsrcMgr "github.com/opencord/voltha-openolt-adapter/internal/pkg/resourcemanager"
+ "github.com/opencord/voltha-openolt-adapter/pkg/mocks"
+ ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ "github.com/opencord/voltha-protos/v3/go/openolt"
+ openoltpb2 "github.com/opencord/voltha-protos/v3/go/openolt"
+ tp_pb "github.com/opencord/voltha-protos/v3/go/tech_profile"
+)
+
+var flowMgr *OpenOltFlowMgr
+
+func init() {
+ log.SetDefaultLogger(log.JSON, log.DebugLevel, nil)
+ flowMgr = newMockFlowmgr()
+}
+func newMockResourceMgr() *resourcemanager.OpenOltResourceMgr {
+ ranges := []*openolt.DeviceInfo_DeviceResourceRanges{
+ {IntfIds: []uint32{0, 1, 2}}}
+
+ deviceinfo := &openolt.DeviceInfo{Vendor: "openolt", Model: "openolt", HardwareVersion: "1.0", FirmwareVersion: "1.0",
+ DeviceId: "olt", DeviceSerialNumber: "openolt", PonPorts: 3, Technology: "Default",
+ OnuIdStart: 1, OnuIdEnd: 1, AllocIdStart: 1, AllocIdEnd: 1,
+ GemportIdStart: 1, GemportIdEnd: 1, FlowIdStart: 1, FlowIdEnd: 1,
+ Ranges: ranges,
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ rsrMgr := resourcemanager.NewResourceMgr(ctx, "olt", "127.0.0.1:2379", "etcd", "olt", deviceinfo)
+ for key := range rsrMgr.ResourceMgrs {
+ rsrMgr.ResourceMgrs[key].KVStore = &db.Backend{}
+ rsrMgr.ResourceMgrs[key].KVStore.Client = &mocks.MockKVClient{}
+ rsrMgr.ResourceMgrs[key].TechProfileMgr = mocks.MockTechProfile{TpID: key}
+ }
+ return rsrMgr
+}
+
+func newMockFlowmgr() *OpenOltFlowMgr {
+ rMgr := newMockResourceMgr()
+ dh := newMockDeviceHandler()
+
+ rMgr.KVStore = &db.Backend{}
+ rMgr.KVStore.Client = &mocks.MockKVClient{}
+
+ dh.resourceMgr = rMgr
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ flwMgr := NewFlowManager(ctx, dh, rMgr)
+
+ onuGemInfo1 := make([]rsrcMgr.OnuGemInfo, 2)
+ onuGemInfo2 := make([]rsrcMgr.OnuGemInfo, 2)
+ onuGemInfo1[0] = rsrcMgr.OnuGemInfo{OnuID: 1, SerialNumber: "1", IntfID: 1, GemPorts: []uint32{1}}
+ onuGemInfo2[1] = rsrcMgr.OnuGemInfo{OnuID: 2, SerialNumber: "2", IntfID: 2, GemPorts: []uint32{2}}
+ flwMgr.onuGemInfo[1] = onuGemInfo1
+ flwMgr.onuGemInfo[2] = onuGemInfo2
+
+ packetInGemPort := make(map[rsrcMgr.PacketInInfoKey]uint32)
+ packetInGemPort[rsrcMgr.PacketInInfoKey{IntfID: 1, OnuID: 1, LogicalPort: 1}] = 1
+ packetInGemPort[rsrcMgr.PacketInInfoKey{IntfID: 2, OnuID: 2, LogicalPort: 2}] = 2
+
+ flwMgr.packetInGemPort = packetInGemPort
+ tps := make(map[uint32]tp.TechProfileIf)
+ for key := range rMgr.ResourceMgrs {
+ tps[key] = mocks.MockTechProfile{TpID: key}
+ }
+ flwMgr.techprofile = tps
+
+ interface2mcastQeueuMap := make(map[uint32]*queueInfoBrief)
+ interface2mcastQeueuMap[0] = &queueInfoBrief{
+ gemPortID: 4000,
+ servicePriority: 3,
+ }
+ flwMgr.interfaceToMcastQueueMap = interface2mcastQeueuMap
+ return flwMgr
+}
+
+func TestOpenOltFlowMgr_CreateSchedulerQueues(t *testing.T) {
+ // flowMgr := newMockFlowmgr()
+
+ tprofile := &tp.TechProfile{Name: "tp1", SubscriberIdentifier: "subscriber1",
+ ProfileType: "pt1", NumGemPorts: 1, Version: 1,
+ InstanceCtrl: tp.InstanceControl{Onu: "1", Uni: "1", MaxGemPayloadSize: "1"},
+ }
+ tprofile.UsScheduler.Direction = "UPSTREAM"
+ tprofile.UsScheduler.AdditionalBw = "AdditionalBW_None"
+ tprofile.UsScheduler.QSchedPolicy = "WRR"
+
+ tprofile2 := tprofile
+ tprofile2.DsScheduler.Direction = "DOWNSTREAM"
+ tprofile2.DsScheduler.AdditionalBw = "AdditionalBW_None"
+ tprofile2.DsScheduler.QSchedPolicy = "WRR"
+ bands := make([]*ofp.OfpMeterBandHeader, 2)
+ bands[0] = &ofp.OfpMeterBandHeader{Type: ofp.OfpMeterBandType_OFPMBT_DROP, Rate: 1000, BurstSize: 5000, Data: &ofp.OfpMeterBandHeader_Drop{}}
+ bands[1] = &ofp.OfpMeterBandHeader{Type: ofp.OfpMeterBandType_OFPMBT_DROP, Rate: 2000, BurstSize: 5000, Data: &ofp.OfpMeterBandHeader_Drop{}}
+ ofpMeterConfig := &ofp.OfpMeterConfig{Flags: 1, MeterId: 1, Bands: bands}
+ flowmetadata := &voltha.FlowMetadata{
+ Meters: []*ofp.OfpMeterConfig{ofpMeterConfig},
+ }
+ type args struct {
+ Dir tp_pb.Direction
+ IntfID uint32
+ OnuID uint32
+ UniID uint32
+ UniPort uint32
+ TpInst *tp.TechProfile
+ MeterID uint32
+ flowMetadata *voltha.FlowMetadata
+ }
+ tests := []struct {
+ name string
+ schedQueue schedQueue
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"CreateSchedulerQueues-1", schedQueue{tp_pb.Direction_UPSTREAM, 1, 1, 1, 64, 1, tprofile, 1, flowmetadata}, false},
+ {"CreateSchedulerQueues-2", schedQueue{tp_pb.Direction_DOWNSTREAM, 1, 1, 1, 65, 1, tprofile2, 1, flowmetadata}, false},
+ {"CreateSchedulerQueues-3", schedQueue{tp_pb.Direction_UPSTREAM, 1, 1, 1, 64, 1, tprofile, 2, flowmetadata}, true},
+ {"CreateSchedulerQueues-4", schedQueue{tp_pb.Direction_DOWNSTREAM, 1, 1, 1, 65, 1, tprofile2, 2, flowmetadata}, true},
+ {"CreateSchedulerQueues-5", schedQueue{tp_pb.Direction_UPSTREAM, 2, 2, 2, 64, 2, tprofile, 2, flowmetadata}, true},
+ {"CreateSchedulerQueues-6", schedQueue{tp_pb.Direction_DOWNSTREAM, 2, 2, 2, 65, 2, tprofile2, 2, flowmetadata}, true},
+ {"CreateSchedulerQueues-13", schedQueue{tp_pb.Direction_DOWNSTREAM, 1, 1, 1, 65, 1, tprofile2, 1, flowmetadata}, false},
+ //Negative testcases
+ {"CreateSchedulerQueues-7", schedQueue{tp_pb.Direction_UPSTREAM, 1, 1, 1, 64, 1, tprofile, 1, &voltha.FlowMetadata{}}, true},
+ {"CreateSchedulerQueues-8", schedQueue{tp_pb.Direction_UPSTREAM, 1, 1, 1, 64, 1, tprofile, 0, &voltha.FlowMetadata{}}, true},
+ {"CreateSchedulerQueues-9", schedQueue{tp_pb.Direction_DOWNSTREAM, 1, 1, 1, 65, 1, tprofile2, 1, &voltha.FlowMetadata{}}, true},
+ {"CreateSchedulerQueues-10", schedQueue{tp_pb.Direction_UPSTREAM, 1, 1, 1, 64, 1, tprofile, 2, &voltha.FlowMetadata{}}, true},
+ {"CreateSchedulerQueues-11", schedQueue{tp_pb.Direction_DOWNSTREAM, 1, 1, 1, 65, 1, tprofile2, 2, &voltha.FlowMetadata{}}, true},
+ {"CreateSchedulerQueues-12", schedQueue{tp_pb.Direction_DOWNSTREAM, 1, 1, 1, 65, 1, tprofile2, 2, nil}, true},
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := flowMgr.CreateSchedulerQueues(ctx, tt.schedQueue); (err != nil) != tt.wantErr {
+ t.Errorf("OpenOltFlowMgr.CreateSchedulerQueues() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_RemoveSchedulerQueues(t *testing.T) {
+
+ // flowMgr := newMockFlowmgr()
+ tprofile := &tp.TechProfile{Name: "tp1", SubscriberIdentifier: "subscriber1",
+ ProfileType: "pt1", NumGemPorts: 1, Version: 1,
+ InstanceCtrl: tp.InstanceControl{Onu: "1", Uni: "1", MaxGemPayloadSize: "1"},
+ }
+ tprofile.UsScheduler.Direction = "UPSTREAM"
+ tprofile.UsScheduler.AdditionalBw = "AdditionalBW_None"
+ tprofile.UsScheduler.QSchedPolicy = "WRR"
+
+ tprofile2 := tprofile
+ tprofile2.DsScheduler.Direction = "DOWNSTREAM"
+ tprofile2.DsScheduler.AdditionalBw = "AdditionalBW_None"
+ tprofile2.DsScheduler.QSchedPolicy = "WRR"
+ //defTprofile := &tp.DefaultTechProfile{}
+ type args struct {
+ Dir tp_pb.Direction
+ IntfID uint32
+ OnuID uint32
+ UniID uint32
+ UniPort uint32
+ TpInst *tp.TechProfile
+ }
+ tests := []struct {
+ name string
+ schedQueue schedQueue
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"RemoveSchedulerQueues", schedQueue{tp_pb.Direction_UPSTREAM, 1, 1, 1, 64, 1, tprofile, 0, nil}, false},
+ {"RemoveSchedulerQueues", schedQueue{tp_pb.Direction_DOWNSTREAM, 1, 1, 1, 65, 1, tprofile2, 0, nil}, false},
+ // negative test cases
+ {"RemoveSchedulerQueues", schedQueue{tp_pb.Direction_DOWNSTREAM, 1, 1, 1, 65, 1, tprofile2, 0, nil}, false},
+ {"RemoveSchedulerQueues", schedQueue{tp_pb.Direction_DOWNSTREAM, 1, 1, 1, 65, 1, tprofile2, 0, nil}, false},
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := flowMgr.RemoveSchedulerQueues(ctx, tt.schedQueue); (err != nil) != tt.wantErr {
+ t.Errorf("OpenOltFlowMgr.RemoveSchedulerQueues() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+
+}
+
+func TestOpenOltFlowMgr_RemoveFlow(t *testing.T) {
+ // flowMgr := newMockFlowmgr()
+ log.Debug("Info Warning Error: Starting RemoveFlow() test")
+ fa := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(2),
+ fu.Metadata_ofp(2),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ },
+ Actions: []*ofp.OfpAction{
+ fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
+ fu.Output(1),
+ },
+ }
+ ofpstats := fu.MkFlowStat(fa)
+ ofpstats.Cookie = ofpstats.Id
+ lldpFa := &fu.FlowArgs{
+ KV: fu.OfpFlowModArgs{"priority": 1000, "cookie": 48132224281636694},
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(1),
+ fu.EthType(0x88CC),
+ fu.TunnelId(536870912),
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
+ },
+ }
+ lldpofpstats := fu.MkFlowStat(lldpFa)
+ //lldpofpstats.Cookie = lldpofpstats.Id
+
+ dhcpFa := &fu.FlowArgs{
+ KV: fu.OfpFlowModArgs{"priority": 1000, "cookie": 48132224281636694},
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(1),
+ fu.UdpSrc(67),
+ //fu.TunnelId(536870912),
+ fu.IpProto(17),
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
+ },
+ }
+ dhcpofpstats := fu.MkFlowStat(dhcpFa)
+ //dhcpofpstats.Cookie = dhcpofpstats.Id
+
+ //multicast flow
+ multicastFa := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(65536),
+ fu.VlanVid(660), //vlan
+ fu.Metadata_ofp(uint64(66)), //inner vlan
+ fu.EthType(0x800), //ipv4
+ fu.Ipv4Dst(3809869825), //227.22.0.1
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Group(1),
+ },
+ }
+ multicastOfpStats := fu.MkFlowStat(multicastFa)
+ multicastOfpStats.Id = 1
+
+ type args struct {
+ flow *ofp.OfpFlowStats
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ // TODO: Add test cases.
+ {"RemoveFlow", args{flow: ofpstats}},
+ {"RemoveFlow", args{flow: lldpofpstats}},
+ {"RemoveFlow", args{flow: dhcpofpstats}},
+ {"RemoveFlow", args{flow: multicastOfpStats}},
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ flowMgr.RemoveFlow(ctx, tt.args.flow)
+ })
+ }
+ // t.Error("=====")
+}
+
+func TestOpenOltFlowMgr_AddFlow(t *testing.T) {
+ // flowMgr := newMockFlowmgr()
+ kw := make(map[string]uint64)
+ kw["table_id"] = 1
+ kw["meter_id"] = 1
+ kw["write_metadata"] = 0x4000000000 // Tech-Profile-ID 64
+
+ // Upstream flow
+ fa := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(536870912),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ },
+ Actions: []*ofp.OfpAction{
+ //fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 257)),
+ fu.Output(65536),
+ fu.PushVlan(0x8100),
+ },
+ KV: kw,
+ }
+
+ // Downstream flow
+ fa3 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(65536),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 257),
+ },
+ Actions: []*ofp.OfpAction{
+ //fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ //fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
+ fu.PopVlan(),
+ fu.Output(536870912),
+ },
+ KV: kw,
+ }
+
+ fa2 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(1000),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ },
+ Actions: []*ofp.OfpAction{
+ //fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
+ fu.Output(65533),
+ },
+ KV: kw,
+ }
+
+ // TODO Add LLDP flow
+ // TODO Add DHCP flow
+
+ // Flows for negative scenarios
+ // Failure in formulateActionInfoFromFlow()
+ fa4 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(1000),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Experimenter(257, []byte{1, 2, 3, 4}),
+ },
+ KV: kw,
+ }
+
+ // Invalid Output
+ fa5 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(1000),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Output(0),
+ },
+ KV: kw,
+ }
+
+ // Tech-Profile-ID update (not supported)
+ kw6 := make(map[string]uint64)
+ kw6["table_id"] = 1
+ kw6["meter_id"] = 1
+ kw6["write_metadata"] = 0x4100000000 // TpID Other than the stored one
+ fa6 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(536870912),
+ fu.TunnelId(16),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ },
+ Actions: []*ofp.OfpAction{
+ //fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 257)),
+ fu.Output(65535),
+ },
+ KV: kw6,
+ }
+
+ lldpFa := &fu.FlowArgs{
+ KV: fu.OfpFlowModArgs{"priority": 1000, "cookie": 48132224281636694},
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(1),
+ fu.EthType(0x88CC),
+ fu.TunnelId(536870912),
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
+ },
+ }
+
+ dhcpFa := &fu.FlowArgs{
+ KV: fu.OfpFlowModArgs{"priority": 1000, "cookie": 48132224281636694},
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(1),
+ fu.UdpSrc(67),
+ //fu.TunnelId(536870912),
+ fu.IpProto(17),
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
+ },
+ }
+ igmpFa := &fu.FlowArgs{
+ KV: fu.OfpFlowModArgs{"priority": 1000, "cookie": 48132224281636694},
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(1),
+ fu.UdpSrc(67),
+ //fu.TunnelId(536870912),
+ fu.IpProto(2),
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
+ },
+ }
+
+ fa9 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(536870912),
+ fu.TunnelId(16),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ fu.VlanPcp(1000),
+ fu.UdpDst(65535),
+ fu.UdpSrc(536870912),
+ fu.Ipv4Dst(65535),
+ fu.Ipv4Src(536870912),
+ },
+ Actions: []*ofp.OfpAction{
+ //fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 257)),
+ fu.Output(65535),
+ },
+ KV: kw6,
+ }
+
+ fa10 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(65533),
+ // fu.TunnelId(16),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ fu.VlanPcp(1000),
+ fu.UdpDst(65535),
+ fu.UdpSrc(536870912),
+ fu.Ipv4Dst(65535),
+ fu.Ipv4Src(536870912),
+ },
+ Actions: []*ofp.OfpAction{
+ //fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 257)),
+ fu.Output(65535),
+ },
+ KV: kw6,
+ }
+ //multicast flow
+ fa11 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(65536),
+ fu.VlanVid(660), //vlan
+ fu.Metadata_ofp(uint64(66)), //inner vlan
+ fu.EthType(0x800), //ipv4
+ fu.Ipv4Dst(3809869825), //227.22.0.1
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Group(1),
+ },
+ KV: kw6,
+ }
+ ofpstats := fu.MkFlowStat(fa)
+ ofpstats2 := fu.MkFlowStat(fa2)
+ ofpstats3 := fu.MkFlowStat(fa3)
+ ofpstats4 := fu.MkFlowStat(fa4)
+ ofpstats5 := fu.MkFlowStat(fa5)
+ ofpstats6 := fu.MkFlowStat(fa6)
+ ofpstats7 := fu.MkFlowStat(lldpFa)
+ ofpstats8 := fu.MkFlowStat(dhcpFa)
+ ofpstats9 := fu.MkFlowStat(fa9)
+ ofpstats10 := fu.MkFlowStat(fa10)
+ igmpstats := fu.MkFlowStat(igmpFa)
+ ofpstats11 := fu.MkFlowStat(fa11)
+
+ fmt.Println(ofpstats6, ofpstats9, ofpstats10)
+
+ ofpMeterConfig := &ofp.OfpMeterConfig{Flags: 1, MeterId: 1}
+ flowMetadata := &voltha.FlowMetadata{
+ Meters: []*ofp.OfpMeterConfig{ofpMeterConfig},
+ }
+ type args struct {
+ flow *ofp.OfpFlowStats
+ flowMetadata *voltha.FlowMetadata
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ // TODO: Add test cases.
+ {"AddFlow", args{flow: ofpstats, flowMetadata: flowMetadata}},
+ {"AddFlow", args{flow: ofpstats2, flowMetadata: flowMetadata}},
+ {"AddFlow", args{flow: ofpstats3, flowMetadata: flowMetadata}},
+ {"AddFlow", args{flow: ofpstats4, flowMetadata: flowMetadata}},
+ {"AddFlow", args{flow: ofpstats5, flowMetadata: flowMetadata}},
+ //{"AddFlow", args{flow: ofpstats6, flowMetadata: flowMetadata}},
+ {"AddFlow", args{flow: ofpstats7, flowMetadata: flowMetadata}},
+ {"AddFlow", args{flow: ofpstats8, flowMetadata: flowMetadata}},
+ //{"AddFlow", args{flow: ofpstats9, flowMetadata: flowMetadata}},
+ {"AddFlow", args{flow: igmpstats, flowMetadata: flowMetadata}},
+ //{"AddFlow", args{flow: ofpstats10, flowMetadata: flowMetadata}},
+ //ofpstats10
+ {"AddFlow", args{flow: ofpstats11, flowMetadata: flowMetadata}},
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ flowMgr.AddFlow(ctx, tt.args.flow, tt.args.flowMetadata)
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_UpdateOnuInfo(t *testing.T) {
+ // flowMgr := newMockFlowmgr()
+ type args struct {
+ intfID uint32
+ onuID uint32
+ serialNum string
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ // TODO: Add test cases.
+ {"UpdateOnuInfo", args{1, 1, "onu1"}},
+ {"UpdateOnuInfo", args{2, 3, "onu1"}},
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+
+ flowMgr.UpdateOnuInfo(ctx, tt.args.intfID, tt.args.onuID, tt.args.serialNum)
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_deleteGemPortFromLocalCache(t *testing.T) {
+ // flowMgr := newMockFlowmgr()
+ type args struct {
+ intfID uint32
+ onuID uint32
+ gemPortIDs []uint32
+ gemPortIDsToBeDeleted []uint32
+ serialNum string
+ finalLength int
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ // Add/Delete single gem port
+ {"DeleteGemPortFromLocalCache1", args{0, 1, []uint32{1}, []uint32{1}, "onu1", 0}},
+ // Delete all gemports
+ {"DeleteGemPortFromLocalCache2", args{0, 1, []uint32{1, 2, 3, 4}, []uint32{1, 2, 3, 4}, "onu1", 0}},
+ // Try to delete when there is no gem port
+ {"DeleteGemPortFromLocalCache3", args{0, 1, []uint32{}, []uint32{1, 2}, "onu1", 0}},
+ // Try to delete non-existent gem port
+ {"DeleteGemPortFromLocalCache4", args{0, 1, []uint32{1}, []uint32{2}, "onu1", 1}},
+ // Try to delete two of the gem ports
+ {"DeleteGemPortFromLocalCache5", args{0, 1, []uint32{1, 2, 3, 4}, []uint32{2, 4}, "onu1", 2}},
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ flowMgr.UpdateOnuInfo(ctx, tt.args.intfID, tt.args.onuID, tt.args.serialNum)
+ for _, gemPort := range tt.args.gemPortIDs {
+ flowMgr.addGemPortToOnuInfoMap(ctx, tt.args.intfID, tt.args.onuID, gemPort)
+ }
+ for _, gemPortDeleted := range tt.args.gemPortIDsToBeDeleted {
+ flowMgr.deleteGemPortFromLocalCache(tt.args.intfID, tt.args.onuID, gemPortDeleted)
+ }
+ lenofGemPorts := len(flowMgr.onuGemInfo[tt.args.intfID][0].GemPorts)
+ if lenofGemPorts != tt.args.finalLength {
+ t.Errorf("GemPorts length is not as expected len = %d, want %d", lenofGemPorts, tt.args.finalLength)
+ }
+
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_GetLogicalPortFromPacketIn(t *testing.T) {
+ // flowMgr := newMockFlowmgr()
+ type args struct {
+ packetIn *openoltpb2.PacketIndication
+ }
+ tests := []struct {
+ name string
+ args args
+ want uint32
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"GetLogicalPortFromPacketIn", args{packetIn: &openoltpb2.PacketIndication{IntfType: "pon", IntfId: 1, GemportId: 1, FlowId: 100, PortNo: 1, Cookie: 100, Pkt: []byte("GetLogicalPortFromPacketIn")}}, 1, false},
+ {"GetLogicalPortFromPacketIn", args{packetIn: &openoltpb2.PacketIndication{IntfType: "nni", IntfId: 1, GemportId: 1, FlowId: 100, PortNo: 1, Cookie: 100, Pkt: []byte("GetLogicalPortFromPacketIn")}}, 1048577, false},
+ // Negative Test cases.
+ {"GetLogicalPortFromPacketIn", args{packetIn: &openoltpb2.PacketIndication{IntfType: "pon", IntfId: 2, GemportId: 1, FlowId: 100, PortNo: 1, Cookie: 100, Pkt: []byte("GetLogicalPortFromPacketIn")}}, 0, true},
+ {"GetLogicalPortFromPacketIn", args{packetIn: &openoltpb2.PacketIndication{IntfType: "pon", IntfId: 1, GemportId: 1, FlowId: 100, PortNo: 0, Cookie: 100, Pkt: []byte("GetLogicalPortFromPacketIn")}}, 4112, false},
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+
+ got, err := flowMgr.GetLogicalPortFromPacketIn(ctx, tt.args.packetIn)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("OpenOltFlowMgr.GetLogicalPortFromPacketIn() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("OpenOltFlowMgr.GetLogicalPortFromPacketIn() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_GetPacketOutGemPortID(t *testing.T) {
+ // flwMgr := newMockFlowmgr()
+
+ type args struct {
+ intfID uint32
+ onuID uint32
+ portNum uint32
+ }
+ tests := []struct {
+ name string
+ args args
+ want uint32
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"GetPacketOutGemPortID", args{intfID: 1, onuID: 1, portNum: 1}, 1, false},
+ {"GetPacketOutGemPortID", args{intfID: 2, onuID: 2, portNum: 2}, 2, false},
+ {"GetPacketOutGemPortID", args{intfID: 1, onuID: 2, portNum: 2}, 0, true},
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+
+ got, err := flowMgr.GetPacketOutGemPortID(ctx, tt.args.intfID, tt.args.onuID, tt.args.portNum)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("OpenOltFlowMgr.GetPacketOutGemPortID() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("OpenOltFlowMgr.GetPacketOutGemPortID() = %v, want %v", got, tt.want)
+ }
+
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_DeleteTechProfileInstance(t *testing.T) {
+ // flwMgr := newMockFlowmgr()
+ type args struct {
+ intfID uint32
+ onuID uint32
+ uniID uint32
+ sn string
+ tpID uint32
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"DeleteTechProfileInstance", args{intfID: 0, onuID: 1, uniID: 1, sn: "", tpID: 64}, false},
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := flowMgr.DeleteTechProfileInstance(ctx, tt.args.intfID, tt.args.onuID, tt.args.uniID, tt.args.sn, tt.args.tpID); (err != nil) != tt.wantErr {
+ t.Errorf("OpenOltFlowMgr.DeleteTechProfileInstance() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_checkAndAddFlow(t *testing.T) {
+ // flowMgr := newMockFlowmgr()
+ kw := make(map[string]uint64)
+ kw["table_id"] = 1
+ kw["meter_id"] = 1
+ kw["write_metadata"] = 0x4000000000 // Tech-Profile-ID 64
+
+ // Upstream flow
+ fa := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(536870912),
+ fu.Metadata_ofp(1),
+ fu.IpProto(17), // dhcp
+ fu.VlanPcp(0),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ },
+ Actions: []*ofp.OfpAction{
+ //fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 257)),
+ fu.Output(65536),
+ fu.PushVlan(0x8100),
+ },
+ KV: kw,
+ }
+
+ // EAPOL
+ fa2 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(536870912),
+ fu.Metadata_ofp(1),
+ fu.EthType(0x888E),
+ fu.VlanPcp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 257),
+ },
+ Actions: []*ofp.OfpAction{
+ //fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 257)),
+ fu.Output(65536),
+ fu.PushVlan(0x8100),
+ },
+ KV: kw,
+ }
+
+ // HSIA
+ fa3 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(536870912),
+ fu.Metadata_ofp(1),
+ //fu.EthType(0x8100),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ },
+ Actions: []*ofp.OfpAction{
+ //fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0)),
+ fu.Output(65536),
+ fu.PushVlan(0x8100),
+ },
+ KV: kw,
+ }
+
+ fa4 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(65535),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+ fu.VlanPcp(1),
+ },
+ Actions: []*ofp.OfpAction{
+ //fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0)),
+ fu.Output(536870912),
+ fu.PopVlan(),
+ },
+ KV: kw,
+ }
+
+ classifierInfo := make(map[string]interface{})
+ actionInfo := make(map[string]interface{})
+ classifierInfo2 := make(map[string]interface{})
+ actionInfo2 := make(map[string]interface{})
+ classifierInfo3 := make(map[string]interface{})
+ actionInfo3 := make(map[string]interface{})
+ classifierInfo4 := make(map[string]interface{})
+ actionInfo4 := make(map[string]interface{})
+ flowState := fu.MkFlowStat(fa)
+ flowState2 := fu.MkFlowStat(fa2)
+ flowState3 := fu.MkFlowStat(fa3)
+ flowState4 := fu.MkFlowStat(fa4)
+ formulateClassifierInfoFromFlow(classifierInfo, flowState)
+ formulateClassifierInfoFromFlow(classifierInfo2, flowState2)
+ formulateClassifierInfoFromFlow(classifierInfo3, flowState3)
+ formulateClassifierInfoFromFlow(classifierInfo4, flowState4)
+
+ err := formulateActionInfoFromFlow(actionInfo, classifierInfo, flowState)
+ if err != nil {
+ // Error logging is already done in the called function
+ // So just return in case of error
+ return
+ }
+
+ err = formulateActionInfoFromFlow(actionInfo2, classifierInfo2, flowState2)
+ if err != nil {
+ // Error logging is already done in the called function
+ // So just return in case of error
+ return
+ }
+
+ err = formulateActionInfoFromFlow(actionInfo3, classifierInfo3, flowState3)
+ if err != nil {
+ // Error logging is already done in the called function
+ // So just return in case of error
+ return
+ }
+
+ err = formulateActionInfoFromFlow(actionInfo4, classifierInfo4, flowState4)
+ if err != nil {
+ // Error logging is already done in the called function
+ // So just return in case of error
+ return
+ }
+
+ //ofpMeterConfig := &ofp.OfpMeterConfig{Flags: 1, MeterId: 1}
+ //flowMetadata := &voltha.FlowMetadata{
+ // Meters: []*ofp.OfpMeterConfig{ofpMeterConfig},
+ //}
+
+ TpInst := &tp.TechProfile{
+ Name: "Test-Tech-Profile",
+ SubscriberIdentifier: "257",
+ ProfileType: "Mock",
+ Version: 1,
+ NumGemPorts: 4,
+ InstanceCtrl: tp.InstanceControl{
+ Onu: "1",
+ Uni: "16",
+ },
+ }
+
+ type fields struct {
+ techprofile []tp.TechProfileIf
+ deviceHandler *DeviceHandler
+ resourceMgr *rsrcMgr.OpenOltResourceMgr
+ }
+ type args struct {
+ args map[string]uint32
+ classifierInfo map[string]interface{}
+ actionInfo map[string]interface{}
+ flow *ofp.OfpFlowStats
+ gemPort uint32
+ intfID uint32
+ onuID uint32
+ uniID uint32
+ portNo uint32
+ TpInst *tp.TechProfile
+ allocID []uint32
+ gemPorts []uint32
+ TpID uint32
+ uni string
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ }{
+ {
+ name: "checkAndAddFlow-1",
+ args: args{
+ args: nil,
+ classifierInfo: classifierInfo,
+ actionInfo: actionInfo,
+ flow: flowState,
+ gemPort: 1,
+ intfID: 1,
+ onuID: 1,
+ uniID: 16,
+ portNo: 1,
+ TpInst: TpInst,
+ allocID: []uint32{0x8001, 0x8002, 0x8003, 0x8004},
+ gemPorts: []uint32{1, 2, 3, 4},
+ TpID: 64,
+ uni: "16",
+ },
+ },
+ {
+ name: "checkAndAddFlow-2",
+ args: args{
+ args: nil,
+ classifierInfo: classifierInfo2,
+ actionInfo: actionInfo2,
+ flow: flowState2,
+ gemPort: 1,
+ intfID: 1,
+ onuID: 1,
+ uniID: 16,
+ portNo: 1,
+ TpInst: TpInst,
+ allocID: []uint32{0x8001, 0x8002, 0x8003, 0x8004},
+ gemPorts: []uint32{1, 2, 3, 4},
+ TpID: 64,
+ uni: "16",
+ },
+ },
+ {
+ name: "checkAndAddFlow-3",
+ args: args{
+ args: nil,
+ classifierInfo: classifierInfo3,
+ actionInfo: actionInfo3,
+ flow: flowState3,
+ gemPort: 1,
+ intfID: 1,
+ onuID: 1,
+ uniID: 16,
+ portNo: 1,
+ TpInst: TpInst,
+ allocID: []uint32{0x8001, 0x8002, 0x8003, 0x8004},
+ gemPorts: []uint32{1, 2, 3, 4},
+ TpID: 64,
+ uni: "16",
+ },
+ },
+ {
+ name: "checkAndAddFlow-4",
+ args: args{
+ args: nil,
+ classifierInfo: classifierInfo4,
+ actionInfo: actionInfo4,
+ flow: flowState4,
+ gemPort: 1,
+ intfID: 1,
+ onuID: 1,
+ uniID: 16,
+ portNo: 1,
+ TpInst: TpInst,
+ allocID: []uint32{0x8001, 0x8002, 0x8003, 0x8004},
+ gemPorts: []uint32{1, 2, 3, 4},
+ TpID: 64,
+ uni: "16",
+ },
+ },
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ flowMgr.checkAndAddFlow(ctx, tt.args.args, tt.args.classifierInfo, tt.args.actionInfo, tt.args.flow,
+ tt.args.TpInst, tt.args.gemPorts, tt.args.TpID, tt.args.uni)
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_TestMulticastFlow(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ //create group
+ group := newGroup(2, []uint32{1})
+ flowMgr.AddGroup(ctx, group)
+
+ //create multicast flow
+ multicastFlowArgs := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(65536),
+ fu.VlanVid(660), //vlan
+ fu.Metadata_ofp(uint64(66)), //inner vlan
+ fu.EthType(0x800), //ipv4
+ fu.Ipv4Dst(3809869825), //227.22.0.1
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Group(1),
+ },
+ }
+ ofpStats := fu.MkFlowStat(multicastFlowArgs)
+ flowMgr.AddFlow(ctx, ofpStats, &voltha.FlowMetadata{})
+
+ //add bucket to the group
+ group = newGroup(2, []uint32{1, 2})
+
+ flowMgr.ModifyGroup(ctx, group)
+}
diff --git a/internal/pkg/core/openolt_test.go b/internal/pkg/core/openolt_test.go
new file mode 100644
index 0000000..5944db2
--- /dev/null
+++ b/internal/pkg/core/openolt_test.go
@@ -0,0 +1,985 @@
+/*
+ * 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.
+ */
+
+/*
+This file contains unit test cases for functions in the file openolt.go.
+This file also implements the fields struct to mock the Openolt and few utility functions.
+*/
+
+//Package core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "context"
+ "errors"
+ com "github.com/opencord/voltha-lib-go/v3/pkg/adapters/common"
+ fu "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+ "github.com/opencord/voltha-lib-go/v3/pkg/kafka"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/config"
+ ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+ "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+ "reflect"
+ "sync"
+ "testing"
+)
+
+// mocks the OpenOLT struct.
+type fields struct {
+ deviceHandlers map[string]*DeviceHandler
+ coreProxy *com.CoreProxy
+ adapterProxy *com.AdapterProxy
+ eventProxy *com.EventProxy
+ kafkaICProxy kafka.InterContainerProxy
+ numOnus int
+ KVStoreHost string
+ KVStorePort int
+ KVStoreType string
+ exitChannel chan int
+ lockDeviceHandlersMap sync.RWMutex
+ ctx context.Context
+}
+
+// mockOlt mocks OpenOLT struct.
+func mockOlt() *fields {
+ dh := newMockDeviceHandler()
+ newOlt := &fields{}
+ newOlt.deviceHandlers = map[string]*DeviceHandler{}
+ newOlt.deviceHandlers[dh.device.Id] = dh
+ return newOlt
+}
+
+// testOltObject maps fields type to OpenOLt type.
+func testOltObject(testOlt *fields) *OpenOLT {
+ return &OpenOLT{
+ deviceHandlers: testOlt.deviceHandlers,
+ coreProxy: testOlt.coreProxy,
+ adapterProxy: testOlt.adapterProxy,
+ eventProxy: testOlt.eventProxy,
+ kafkaICProxy: testOlt.kafkaICProxy,
+ numOnus: testOlt.numOnus,
+ KVStoreHost: testOlt.KVStoreHost,
+ KVStorePort: testOlt.KVStorePort,
+ KVStoreType: testOlt.KVStoreType,
+ exitChannel: testOlt.exitChannel,
+ }
+}
+
+// mockDevice mocks Device.
+func mockDevice() *voltha.Device {
+ device := &voltha.Device{
+ Id: "olt",
+ Root: true,
+ ParentId: "logical_device",
+ Ports: []*voltha.Port{
+ {PortNo: 1, Label: "pon"},
+ {PortNo: 2, Label: "nni"},
+ },
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ DeviceId: "olt",
+ DeviceType: "onu",
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ },
+ ConnectStatus: 1,
+ }
+ return device
+}
+
+func TestNewOpenOLT(t *testing.T) {
+ tests := []struct {
+ name string
+ fields *fields
+ configFlags *config.AdapterFlags
+ want *OpenOLT
+ }{
+ {"newopenolt-1", &fields{}, &config.AdapterFlags{OnuNumber: 1, KVStorePort: 1, KVStoreType: "consul", KVStoreHost: "1.1.1.1"},
+ &OpenOLT{numOnus: 1, KVStorePort: 1, KVStoreType: "consul", KVStoreHost: "1.1.1.1"}},
+ {"newopenolt-2", &fields{}, &config.AdapterFlags{OnuNumber: 2, KVStorePort: 2, KVStoreType: "etcd", KVStoreHost: "2.2.2.2"},
+ &OpenOLT{numOnus: 2, KVStorePort: 2, KVStoreType: "etcd", KVStoreHost: "2.2.2.2"}},
+ {"newopenolt-3", &fields{}, &config.AdapterFlags{OnuNumber: 3, KVStorePort: 3, KVStoreType: "consul", KVStoreHost: "3.3.3.3"},
+ &OpenOLT{numOnus: 3, KVStorePort: 3, KVStoreType: "consul", KVStoreHost: "3.3.3.3"}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := NewOpenOLT(tt.fields.ctx, tt.fields.kafkaICProxy, tt.fields.coreProxy, tt.fields.adapterProxy,
+ tt.fields.eventProxy, tt.configFlags); reflect.TypeOf(got) != reflect.TypeOf(tt.want) && got != nil {
+ t.Errorf("NewOpenOLT() error = %v, wantErr %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Abandon_device(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"abandon_device-1", &fields{}, args{}, ErrNotImplemented},
+ {"abandon_device-2", &fields{}, args{}, ErrNotImplemented},
+ {"abandon_device-3", &fields{}, args{}, ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Abandon_device(tt.args.device); err != tt.wantErr {
+ t.Errorf("Abandon_device() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Activate_image_update(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ request *voltha.ImageDownload
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want *voltha.ImageDownload
+ wantErr error
+ }{
+ {"activate_image_upate-1", &fields{}, args{}, &voltha.ImageDownload{Id: "Image1-ABC123XYZ"},
+ ErrNotImplemented},
+ {"activate_image_upate-2", &fields{}, args{}, &voltha.ImageDownload{Id: "Image2-ABC123CDE"},
+ ErrNotImplemented},
+ {"activate_image_upate-3", &fields{}, args{}, &voltha.ImageDownload{Id: "Image3-ABC123EFG"},
+ ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Activate_image_update(tt.args.device, tt.args.request)
+ if err != tt.wantErr && got == nil {
+ t.Errorf("Activate_image_update() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Adapter_descriptor(t *testing.T) {
+ tests := []struct {
+ name string
+ fields *fields
+ wantErr error
+ }{
+ {"adapter_descriptor-1", &fields{}, ErrNotImplemented},
+ {"adapter_descriptor-2", &fields{}, ErrNotImplemented},
+ {"adapter_descriptor-3", &fields{}, ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Adapter_descriptor(); err != tt.wantErr {
+ t.Errorf("Adapter_descriptor() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Adopt_device(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ }
+ var device = mockDevice()
+ device.Id = "olt"
+ nilDevice := NewErrInvalidValue(log.Fields{"device": nil}, nil)
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"adopt_device-1", mockOlt(), args{}, nilDevice},
+ {"adopt_device-2", mockOlt(), args{device}, nilDevice},
+ {"adopt_device-3", mockOlt(), args{mockDevice()}, nil},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ err := oo.Adopt_device(tt.args.device)
+ if (err != nil) && (reflect.TypeOf(err) !=
+ reflect.TypeOf(tt.wantErr)) && (tt.args.device == nil) {
+ t.Errorf("Adopt_device() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if err == nil {
+ t.Log("return'd nil")
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Cancel_image_download(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ request *voltha.ImageDownload
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want *voltha.ImageDownload
+ wantErr error
+ }{
+ {"cancel_image_download-1", &fields{}, args{}, &voltha.ImageDownload{Id: "Image1-ABC123XYZ"},
+ ErrNotImplemented},
+ {"cancel_image_download-2", &fields{}, args{}, &voltha.ImageDownload{Id: "Image2-ABC123IJK"},
+ ErrNotImplemented},
+ {"cancel_image_download-3", &fields{}, args{}, &voltha.ImageDownload{Id: "Image3-ABC123KLM"},
+ ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Cancel_image_download(tt.args.device, tt.args.request)
+ if err != tt.wantErr && got == nil {
+ t.Errorf("Cancel_image_download() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Delete_device(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"delete_device-1", &fields{}, args{mockDevice()},
+ NewErrNotFound("device-handler", log.Fields{"device-id": "olt"}, nil)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Delete_device(tt.args.device); !reflect.DeepEqual(err, tt.wantErr) {
+ t.Errorf("Delete_device() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Device_types(t *testing.T) {
+ tests := []struct {
+ name string
+ fields *fields
+ want *voltha.DeviceTypes
+ wantErr error
+ }{
+ {"device_types-1", &fields{}, &voltha.DeviceTypes{},
+ ErrNotImplemented},
+ {"device_types-2", &fields{}, &voltha.DeviceTypes{},
+ ErrNotImplemented},
+ {"device_types-3", &fields{}, &voltha.DeviceTypes{},
+ ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Device_types()
+ if err != tt.wantErr && got == nil {
+ t.Errorf("Device_types() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Disable_device(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"disable_device-1", mockOlt(), args{mockDevice()}, nil},
+ {"disable_device-2", &fields{}, args{mockDevice()},
+ NewErrNotFound("device-handler", log.Fields{"device-id": "olt"}, nil)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Disable_device(tt.args.device); !reflect.DeepEqual(err, tt.wantErr) {
+ t.Errorf("Disable_device() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Download_image(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ request *voltha.ImageDownload
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want *voltha.ImageDownload
+ wantErr error
+ }{
+ {"download_image-1", &fields{}, args{}, &voltha.ImageDownload{Id: "Image1-ABC123XYZ"},
+ ErrNotImplemented},
+ {"download_image-2", &fields{}, args{}, &voltha.ImageDownload{Id: "Image2-ABC123LKJ"},
+ ErrNotImplemented},
+ {"download_image-3", &fields{}, args{}, &voltha.ImageDownload{Id: "Image1-ABC123RTY"},
+ ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Download_image(tt.args.device, tt.args.request)
+ if err != tt.wantErr && got == nil {
+ t.Errorf("Download_image() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Get_device_details(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"get_device_details-1", &fields{}, args{}, ErrNotImplemented},
+ {"get_device_details-2", &fields{}, args{}, ErrNotImplemented},
+ {"get_device_details-3", &fields{}, args{}, ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Get_device_details(tt.args.device); err != tt.wantErr {
+ t.Errorf("Get_device_details() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Get_image_download_status(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ request *voltha.ImageDownload
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want *voltha.ImageDownload
+ wantErr error
+ }{
+ {"get_image_download_status-1", &fields{}, args{}, &voltha.ImageDownload{Id: "Image1-ABC123XYZ"},
+ ErrNotImplemented},
+ {"get_image_download_status-2", &fields{}, args{}, &voltha.ImageDownload{Id: "Image2-ABC123LKJ"},
+ ErrNotImplemented},
+ {"get_image_download_status-3", &fields{}, args{}, &voltha.ImageDownload{Id: "Image1-ABC123DFG"},
+ ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Get_image_download_status(tt.args.device, tt.args.request)
+ if err != tt.wantErr && got == nil {
+ t.Errorf("Get_image_download_status() got = %v want = %v error = %v, wantErr %v",
+ got, tt.want, err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Get_ofp_device_info(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want *ic.SwitchCapability
+ wantErr error
+ }{
+ {"get_ofp_device_info-1", mockOlt(), args{mockDevice()}, &ic.SwitchCapability{
+ Desc: &openflow_13.OfpDesc{
+ MfrDesc: "VOLTHA Project",
+ HwDesc: "open_pon",
+ SwDesc: "open_pon",
+ },
+ SwitchFeatures: &openflow_13.OfpSwitchFeatures{
+ NBuffers: uint32(256),
+ NTables: uint32(2),
+ Capabilities: uint32(15),
+ },
+ }, nil},
+ {"get_ofp_device_info-2", &fields{}, args{mockDevice()}, nil,
+ NewErrNotFound("device-handler", log.Fields{"device-id": "olt"}, nil)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Get_ofp_device_info(tt.args.device)
+ if !reflect.DeepEqual(err, tt.wantErr) || !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("Get_ofp_device_info() got = %v want = %v error = %v, wantErr = %v",
+ got, tt.want, err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Get_ofp_port_info(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ portNo int64
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want *ic.PortCapability
+ wantErr error
+ }{
+ {"get_ofp_port_info-1", mockOlt(), args{mockDevice(), 1}, &ic.PortCapability{
+ Port: &voltha.LogicalPort{
+ DeviceId: "olt",
+ DevicePortNo: uint32(1),
+ OfpPort: &openflow_13.OfpPort{
+ HwAddr: []uint32{1, 2, 3, 4, 5, 6},
+ State: uint32(4),
+ Curr: uint32(4128),
+ Advertised: uint32(4128),
+ Peer: uint32(4128),
+ CurrSpeed: uint32(32),
+ MaxSpeed: uint32(32),
+ },
+ },
+ }, nil},
+ {"get_ofp_port_info-2", &fields{}, args{mockDevice(), 1}, nil,
+ NewErrNotFound("device-handler", log.Fields{"device-id": "olt"}, nil)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Get_ofp_port_info(tt.args.device, tt.args.portNo)
+ if !reflect.DeepEqual(err, tt.wantErr) || !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("Get_ofp_port_info() got = %v want = %v error = %v, wantErr = %v",
+ got, tt.want, err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Health(t *testing.T) {
+ tests := []struct {
+ name string
+ fields *fields
+ want *voltha.HealthStatus
+ wantErr error
+ }{
+ {"health-1", &fields{}, &voltha.HealthStatus{}, ErrNotImplemented},
+ {"health-2", &fields{}, &voltha.HealthStatus{}, ErrNotImplemented},
+ {"health-3", &fields{}, &voltha.HealthStatus{}, ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Health()
+ if err != tt.wantErr && got == nil {
+ t.Errorf("Get_ofp_port_info() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Process_inter_adapter_message(t *testing.T) {
+ type args struct {
+ msg *ic.InterAdapterMessage
+ }
+ var message1 = args{
+ msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "olt",
+ ProxyDeviceId: "",
+ ToDeviceId: "onu1",
+ },
+ },
+ }
+ var message2 = args{
+ msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "olt",
+ ProxyDeviceId: "olt",
+ ToDeviceId: "olt",
+ Type: ic.InterAdapterMessageType_OMCI_REQUEST,
+ },
+ },
+ }
+ var message3 = args{
+ msg: &ic.InterAdapterMessage{
+ Header: &ic.InterAdapterHeader{
+ Id: "olt",
+ ProxyDeviceId: "olt",
+ ToDeviceId: "olt",
+ Type: ic.InterAdapterMessageType_FLOW_REQUEST,
+ },
+ },
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErrType reflect.Type
+ }{
+ {"process_inter_adaptor_messgae-1", mockOlt(), message1,
+ reflect.TypeOf(&ErrNotFound{})},
+ {"process_inter_adaptor_messgae-2", mockOlt(), message2,
+ reflect.TypeOf(errors.New("message is nil"))},
+ {"process_inter_adaptor_messgae-3", mockOlt(), message3,
+ reflect.TypeOf(&ErrInvalidValue{})},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Process_inter_adapter_message(tt.args.msg); reflect.TypeOf(err) != tt.wantErrType {
+ t.Errorf("Process_inter_adapter_message() error = %v, wantErr %v",
+ reflect.TypeOf(err), tt.wantErrType)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Reboot_device(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"reboot_device-1", mockOlt(), args{mockDevice()}, nil},
+ {"reboot_device-2", &fields{}, args{mockDevice()},
+ NewErrNotFound("device-handler", log.Fields{"device-id": "olt"}, nil)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Reboot_device(tt.args.device); !reflect.DeepEqual(err, tt.wantErr) {
+ t.Errorf("Reboot_device() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Receive_packet_out(t *testing.T) {
+ acts := []*ofp.OfpAction{
+ fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
+ fu.Output(1),
+ }
+ type args struct {
+ deviceID string
+ egressPortNo int
+ packet *openflow_13.OfpPacketOut
+ }
+ pktout := &ofp.OfpPacketOut{BufferId: 0, InPort: 1, Actions: acts, Data: []byte("AYDCAAAOAODsSE5TiMwCBwQA4OxITlIEBQUwLzUx" +
+ "BgIAFAgEMC81MQoJbG9jYWxob3N0EBwFAawbqqACAAAAoRAxLjMuNi4xLjQuMS40NDEz/gYAgMILAgD+GQCAwgkDAAAAAGQAAAAAAAAAAgICAgICAgL+" +
+ "GQCAwgoDAAAAAGQAAAAAAAAAAgICAgICAgIAAA==")}
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"receive_packet_out-1", mockOlt(), args{mockDevice().Id, 1, pktout}, nil},
+ {"receive_packet_out-2", mockOlt(), args{"1234", 1, pktout},
+ NewErrNotFound("device-handler", log.Fields{"device-id": "1234"}, nil)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Receive_packet_out(tt.args.deviceID, tt.args.egressPortNo, tt.args.packet); !reflect.DeepEqual(err, tt.wantErr) {
+ t.Errorf("Receive_packet_out() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Reconcile_device(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ }
+ expectedError := NewErrInvalidValue(log.Fields{"device": nil}, nil)
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"reconcile_device-1", &fields{}, args{}, expectedError},
+ {"reconcile_device-2", &fields{}, args{}, expectedError},
+ {"reconcile_device-3", &fields{}, args{}, expectedError},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Reconcile_device(tt.args.device); !reflect.DeepEqual(err, tt.wantErr) {
+ t.Errorf("Reconcile_device() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Reenable_device(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"reenable_device-1", mockOlt(), args{mockDevice()}, nil},
+ {"reenable_device-2", &fields{}, args{mockDevice()},
+ NewErrNotFound("device-handler", log.Fields{"device-id": "olt"}, nil)},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Reenable_device(tt.args.device); !reflect.DeepEqual(err, tt.wantErr) {
+ t.Errorf("Reenable_device() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Revert_image_update(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ request *voltha.ImageDownload
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want *voltha.ImageDownload
+ wantErr error
+ }{
+ {"revert_image_update-1", &fields{}, args{}, &voltha.ImageDownload{Id: "Image1-ABC123XYZ"},
+ ErrNotImplemented},
+ {"revert_image_update-2", &fields{}, args{}, &voltha.ImageDownload{Id: "Image2-ABC123TYU"},
+ ErrNotImplemented},
+ {"revert_image_update-3", &fields{}, args{}, &voltha.ImageDownload{Id: "Image3-ABC123GTH"},
+ ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Revert_image_update(tt.args.device, tt.args.request)
+ if err != tt.wantErr && got == nil {
+ t.Log("error :", err)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Self_test_device(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"self_test_device-1", &fields{}, args{}, ErrNotImplemented},
+ {"self_test_device-2", &fields{}, args{}, ErrNotImplemented},
+ {"self_test_device-3", &fields{}, args{}, ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Self_test_device(tt.args.device); err != tt.wantErr {
+ t.Errorf("Self_test_device() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Start(t *testing.T) {
+ type args struct {
+ ctx context.Context
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"start-1", &fields{}, args{}, errors.New("start error")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Start(tt.args.ctx); err != nil {
+ t.Errorf("Start() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Stop(t *testing.T) {
+ type args struct {
+ ctx context.Context
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"stop-1", &fields{exitChannel: make(chan int, 1)}, args{}, errors.New("stop error")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ oo.Start(tt.args.ctx)
+ if err := oo.Stop(tt.args.ctx); err != nil {
+ t.Errorf("Stop() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Suppress_event(t *testing.T) {
+ type args struct {
+ filter *voltha.EventFilter
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"suppress_event-1", &fields{}, args{}, ErrNotImplemented},
+ {"suppress_event-2", &fields{}, args{}, ErrNotImplemented},
+ {"suppress_event-3", &fields{}, args{}, ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Suppress_event(tt.args.filter); err != tt.wantErr {
+ t.Errorf("Suppress_event() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Unsuppress_event(t *testing.T) {
+ type args struct {
+ filter *voltha.EventFilter
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"unsupress_event-1", &fields{}, args{}, ErrNotImplemented},
+ {"unsupress_event-2", &fields{}, args{}, ErrNotImplemented},
+ {"unsupress_event-3", &fields{}, args{}, ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Unsuppress_event(tt.args.filter); err != tt.wantErr {
+ t.Errorf("Unsuppress_event() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Update_flows_bulk(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ flows *voltha.Flows
+ groups *voltha.FlowGroups
+ flowMetadata *voltha.FlowMetadata
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"update_flows_bulk-1", &fields{}, args{}, ErrNotImplemented},
+ {"update_flows_bulk-2", &fields{}, args{}, ErrNotImplemented},
+ {"update_flows_bulk-3", &fields{}, args{}, ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Update_flows_bulk(tt.args.device, tt.args.flows, tt.args.groups, tt.args.flowMetadata); err != tt.wantErr {
+ t.Errorf("Update_flows_bulk() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Update_flows_incrementally(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ flows *openflow_13.FlowChanges
+ groups *openflow_13.FlowGroupChanges
+ flowMetadata *voltha.FlowMetadata
+ }
+
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"update_flows_incrementally-1", &fields{}, args{device: mockDevice()},
+ NewErrNotFound("device-handler", log.Fields{"device-id": "olt"}, nil)},
+ {"update_flows_incrementally-2", mockOlt(), args{device: mockDevice()}, nil},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Update_flows_incrementally(tt.args.device, tt.args.flows, tt.args.groups, tt.args.flowMetadata); !reflect.DeepEqual(err, tt.wantErr) {
+ t.Errorf("Update_flows_incrementally() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Update_pm_config(t *testing.T) {
+ type args struct {
+ device *voltha.Device
+ pmConfigs *voltha.PmConfigs
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"update_pm_config-1", &fields{}, args{}, ErrNotImplemented},
+ {"update_pm_config-2", &fields{}, args{}, ErrNotImplemented},
+ {"update_pm_config-3", &fields{}, args{}, ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Update_pm_config(tt.args.device, tt.args.pmConfigs); err != tt.wantErr {
+ t.Errorf("Update_pm_config() error = %v, wantErr %v", err, tt.wantErr)
+ }
+
+ })
+ }
+}
+
+func TestOpenOLT_deleteDeviceHandlerToMap(t *testing.T) {
+ type args struct {
+ agent *DeviceHandler
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ }{
+ {"delete_device_handler_map-1", mockOlt(), args{newMockDeviceHandler()}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ oo.deleteDeviceHandlerToMap(tt.args.agent)
+ if len(oo.deviceHandlers) > 0 {
+ t.Errorf("delete device manager failed")
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Enable_port(t *testing.T) {
+ type args struct {
+ deviceID string
+ port *voltha.Port
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"Enable_port-1", mockOlt(), args{deviceID: "olt", port: &voltha.Port{Type: voltha.Port_PON_OLT, PortNo: 1}}, false},
+ {"Enable_port-2", mockOlt(), args{deviceID: "olt", port: &voltha.Port{Type: voltha.Port_ETHERNET_NNI, PortNo: 1}}, true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Enable_port(tt.args.deviceID, tt.args.port); (err != nil) != tt.wantErr {
+ t.Errorf("OpenOLT.Enable_port() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOLT_Disable_port(t *testing.T) {
+ type args struct {
+ deviceID string
+ port *voltha.Port
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"Disable_port-1", mockOlt(), args{deviceID: "olt", port: &voltha.Port{Type: voltha.Port_PON_OLT, PortNo: 1}}, false},
+ {"Disable_port-2", mockOlt(), args{deviceID: "olt", port: &voltha.Port{Type: voltha.Port_ETHERNET_NNI, PortNo: 1}}, true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Disable_port(tt.args.deviceID, tt.args.port); (err != nil) != tt.wantErr {
+ t.Errorf("OpenOLT.Disable_port() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
diff --git a/internal/pkg/core/statsmanager.go b/internal/pkg/core/statsmanager.go
new file mode 100755
index 0000000..3133bce
--- /dev/null
+++ b/internal/pkg/core/statsmanager.go
@@ -0,0 +1,486 @@
+/*
+ * Copyright 2019-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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "fmt"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "github.com/opencord/voltha-protos/v3/go/openolt"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+ "sync"
+ "time"
+)
+
+var mutex = &sync.Mutex{}
+
+// PonPort representation
+type PonPort struct {
+ /*
+ This is a highly reduced version taken from the adtran pon_port.
+ TODO: Extend for use in the openolt adapter set.
+ */
+ /* MAX_ONUS_SUPPORTED = 256
+ DEFAULT_ENABLED = False
+ MAX_DEPLOYMENT_RANGE = 25000 # Meters (OLT-PB maximum)
+
+ _MCAST_ONU_ID = 253
+ _MCAST_ALLOC_BASE = 0x500
+
+ _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery'] # , 'autoactivate']
+ _SUPPORTED_AUTHENTICATION_METHODS = ['serial-number']
+ */
+ PONID uint32
+ DeviceID string
+ IntfID uint32
+ PortNum uint32
+ PortID uint32
+ Label string
+ ONUs map[uint32]interface{}
+ ONUsByID map[uint32]interface{}
+
+ RxBytes uint64
+ RxPackets uint64
+ RxUcastPackets uint64
+ RxMcastPackets uint64
+ RxBcastPackets uint64
+ RxErrorPackets uint64
+ TxBytes uint64
+ TxPackets uint64
+ TxUcastPackets uint64
+ TxMcastPackets uint64
+ TxBcastPackets uint64
+ TxErrorPackets uint64
+ RxCrcErrors uint64
+ BipErrors uint64
+}
+
+// NewPONPort returns a new instance of PonPort initialized with given PONID, DeviceID, IntfID and PortNum
+func NewPONPort(PONID uint32, DeviceID string, IntfID uint32, PortNum uint32) *PonPort {
+
+ var PON PonPort
+
+ PON.PONID = PONID
+ PON.DeviceID = DeviceID
+ PON.IntfID = IntfID
+ PON.PortNum = PortNum
+ PON.PortID = 0
+ PON.Label = fmt.Sprintf("%s%d", "pon-", PONID)
+
+ PON.ONUs = make(map[uint32]interface{})
+ PON.ONUsByID = make(map[uint32]interface{})
+
+ /*
+ Statistics taken from nni_port
+ self.intf_id = 0 #handled by getter
+ self.port_no = 0 #handled by getter
+ self.port_id = 0 #handled by getter
+
+ Note: In the current implementation of the kpis coming from the BAL the stats are the
+ samne model for NNI and PON.
+
+ TODO: Integrate additional kpis for the PON and other southbound port objecgts.
+
+ */
+
+ PON.RxBytes = 0
+ PON.RxPackets = 0
+ PON.RxUcastPackets = 0
+ PON.RxMcastPackets = 0
+ PON.RxBcastPackets = 0
+ PON.RxErrorPackets = 0
+ PON.TxBytes = 0
+ PON.TxPackets = 0
+ PON.TxUcastPackets = 0
+ PON.TxMcastPackets = 0
+ PON.TxBcastPackets = 0
+ PON.TxErrorPackets = 0
+ PON.RxCrcErrors = 0
+ PON.BipErrors = 0
+
+ /* def __str__(self):
+ return "PonPort-{}: Admin: {}, Oper: {}, OLT: {}".format(self._label,
+ self._admin_state,
+ self._oper_status,
+ self.olt)
+ */
+ return &PON
+}
+
+// NniPort representation
+type NniPort struct {
+ /*
+ Northbound network port, often Ethernet-based
+
+ This is a highly reduced version taken from the adtran nni_port code set
+ TODO: add functions to allow for port specific values and operations
+ */
+ PortNum uint32
+ Name string
+ LogicalPort uint32
+ IntfID uint32
+
+ RxBytes uint64
+ RxPackets uint64
+ RxUcastPackets uint64
+ RxMcastPackets uint64
+ RxBcastPackets uint64
+ RxErrorPackets uint64
+ TxBytes uint64
+ TxPackets uint64
+ TxUcastPackets uint64
+ TxMcastPackets uint64
+ TxBcastPackets uint64
+ TxErrorPackets uint64
+ RxCrcErrors uint64
+ BipErrors uint64
+}
+
+// NewNniPort returns a new instance of NniPort initialized with the given PortNum and IntfID
+func NewNniPort(PortNum uint32, IntfID uint32) *NniPort {
+
+ var NNI NniPort
+
+ NNI.PortNum = PortNum
+ NNI.Name = fmt.Sprintf("%s%d", "nni-", PortNum)
+ NNI.IntfID = IntfID
+
+ NNI.RxBytes = 0
+ NNI.RxPackets = 0
+ NNI.RxUcastPackets = 0
+ NNI.RxMcastPackets = 0
+ NNI.RxBcastPackets = 0
+ NNI.RxErrorPackets = 0
+ NNI.TxBytes = 0
+ NNI.TxPackets = 0
+ NNI.TxUcastPackets = 0
+ NNI.TxMcastPackets = 0
+ NNI.TxBcastPackets = 0
+ NNI.TxErrorPackets = 0
+ NNI.RxCrcErrors = 0
+ NNI.BipErrors = 0
+
+ return &NNI
+}
+
+// OpenOltStatisticsMgr structure
+type OpenOltStatisticsMgr struct {
+ Device *DeviceHandler
+ NorthBoundPort map[uint32]*NniPort
+ SouthBoundPort map[uint32]*PonPort
+ // TODO PMMetrics Metrics
+}
+
+// NewOpenOltStatsMgr returns a new instance of the OpenOltStatisticsMgr
+func NewOpenOltStatsMgr(Dev *DeviceHandler) *OpenOltStatisticsMgr {
+
+ var StatMgr OpenOltStatisticsMgr
+
+ StatMgr.Device = Dev
+ // TODO call metric PMMetric =
+ // Northbound and Southbound ports
+ // added to initialize the pm_metrics
+ var Ports interface{}
+ Ports, _ = InitPorts("nni", Dev.deviceID, 1)
+ StatMgr.NorthBoundPort, _ = Ports.(map[uint32]*NniPort)
+ NumPonPorts := Dev.resourceMgr.DevInfo.GetPonPorts()
+ Ports, _ = InitPorts("pon", Dev.deviceID, NumPonPorts)
+ StatMgr.SouthBoundPort, _ = Ports.(map[uint32]*PonPort)
+ return &StatMgr
+}
+
+// InitPorts collects the port objects: nni and pon that are updated with the current data from the OLT
+func InitPorts(Intftype string, DeviceID string, numOfPorts uint32) (interface{}, error) {
+ /*
+ This method collects the port objects: nni and pon that are updated with the
+ current data from the OLT
+
+ Both the northbound (nni) and southbound ports are indexed by the interface id (intf_id)
+ and NOT the port number. When the port object is instantiated it will contain the intf_id and
+ port_no values
+
+ :param type:
+ :return:
+ */
+ var i uint32
+ if Intftype == "nni" {
+ NniPorts := make(map[uint32]*NniPort)
+ for i = 0; i < numOfPorts; i++ {
+ Port := BuildPortObject(i, "nni", DeviceID).(*NniPort)
+ NniPorts[Port.IntfID] = Port
+ }
+ return NniPorts, nil
+ } else if Intftype == "pon" {
+ PONPorts := make(map[uint32]*PonPort)
+ for i = 0; i < numOfPorts; i++ {
+ PONPort := BuildPortObject(i, "pon", DeviceID).(*PonPort)
+ PONPorts[PortNoToIntfID(PONPort.IntfID, voltha.Port_PON_OLT)] = PONPort
+ }
+ return PONPorts, nil
+ } else {
+ log.Errorf("Invalid type of interface %s", Intftype)
+ return nil, NewErrInvalidValue(log.Fields{"interface-type": Intftype}, nil)
+ }
+}
+
+// BuildPortObject allows for updating north and southbound ports, newly discovered ports, and devices
+func BuildPortObject(PortNum uint32, IntfType string, DeviceID string) interface{} {
+ /*
+ Separate method to allow for updating north and southbound ports
+ newly discovered ports and devices
+
+ :param port_num:
+ :param type:
+ :return:
+ */
+
+ //This builds a port object which is added to the
+ //appropriate northbound or southbound values
+ if IntfType == "nni" {
+ IntfID := IntfIDToPortNo(PortNum, voltha.Port_ETHERNET_NNI)
+ nniID := PortNoToIntfID(IntfID, voltha.Port_ETHERNET_NNI)
+ log.Debugf("NniID %v", nniID)
+ return NewNniPort(PortNum, nniID)
+ } else if IntfType == "pon" {
+ // PON ports require a different configuration
+ // intf_id and pon_id are currently equal.
+ IntfID := IntfIDToPortNo(PortNum, voltha.Port_PON_OLT)
+ PONID := PortNoToIntfID(IntfID, voltha.Port_PON_OLT)
+ log.Debugf("PonID %v", PONID)
+ return NewPONPort(PONID, DeviceID, IntfID, PortNum)
+ } else {
+ log.Errorf("Invalid type of interface %s", IntfType)
+ return nil
+ }
+}
+
+// collectNNIMetrics will collect the nni port metrics
+func (StatMgr *OpenOltStatisticsMgr) collectNNIMetrics(nniID uint32) map[string]float32 {
+
+ nnival := make(map[string]float32)
+ mutex.Lock()
+ cm := StatMgr.Device.portStats.NorthBoundPort[nniID]
+ mutex.Unlock()
+ metricName := StatMgr.Device.metrics.GetSubscriberMetrics()
+
+ if metricName != nil && len(metricName) > 0 {
+ for mName := range metricName {
+ switch mName {
+ case "rx_bytes":
+ nnival["RxBytes"] = float32(cm.RxBytes)
+ case "rx_packets":
+ nnival["RxPackets"] = float32(cm.RxPackets)
+ case "rx_ucast_packets":
+ nnival["RxUcastPackets"] = float32(cm.RxUcastPackets)
+ case "rx_mcast_packets":
+ nnival["RxMcastPackets"] = float32(cm.RxMcastPackets)
+ case "rx_bcast_packets":
+ nnival["RxBcastPackets"] = float32(cm.RxBcastPackets)
+ case "tx_bytes":
+ nnival["TxBytes"] = float32(cm.TxBytes)
+ case "tx_packets":
+ nnival["TxPackets"] = float32(cm.TxPackets)
+ case "tx_mcast_packets":
+ nnival["TxMcastPackets"] = float32(cm.TxMcastPackets)
+ case "tx_bcast_packets":
+ nnival["TxBcastPackets"] = float32(cm.TxBcastPackets)
+ }
+ }
+ }
+ return nnival
+}
+
+// collectPONMetrics will collect the pon port metrics
+func (StatMgr *OpenOltStatisticsMgr) collectPONMetrics(pID uint32) map[string]float32 {
+
+ ponval := make(map[string]float32)
+ mutex.Lock()
+ cm := StatMgr.Device.portStats.SouthBoundPort[pID]
+ mutex.Unlock()
+ metricName := StatMgr.Device.metrics.GetSubscriberMetrics()
+
+ if metricName != nil && len(metricName) > 0 {
+ for mName := range metricName {
+ switch mName {
+ case "rx_bytes":
+ ponval["RxBytes"] = float32(cm.RxBytes)
+ case "rx_packets":
+ ponval["RxPackets"] = float32(cm.RxPackets)
+ // these are not supported in OpenOlt Agent now
+ // will return zero until supported
+ case "rx_ucast_packets":
+ ponval["RxUcastPackets"] = float32(cm.RxUcastPackets)
+ case "rx_mcast_packets":
+ ponval["RxMcastPackets"] = float32(cm.RxMcastPackets)
+ case "rx_bcast_packets":
+ ponval["RxBcastPackets"] = float32(cm.RxBcastPackets)
+ // End will return zero until supported
+ case "tx_bytes":
+ ponval["TxBytes"] = float32(cm.TxBytes)
+ case "tx_packets":
+ ponval["TxPackets"] = float32(cm.TxPackets)
+ // these are not supported in OpenOlt Agent now
+ // will return zero until supported
+ case "tx_ucast_packets":
+ ponval["TxUcastPackets"] = float32(cm.TxUcastPackets)
+ case "tx_mcast_packets":
+ ponval["TxMcastPackets"] = float32(cm.TxMcastPackets)
+ case "tx_bcast_packets":
+ ponval["TxBcastPackets"] = float32(cm.TxBcastPackets)
+ }
+ }
+ }
+ return ponval
+}
+
+// publishMatrics will publish the pon port metrics
+func (StatMgr OpenOltStatisticsMgr) publishMetrics(portType string, val map[string]float32, portnum uint32, context map[string]string, devID string) {
+ log.Debugf("Post-%v %v", portType, val)
+
+ var metricInfo voltha.MetricInformation
+ var ke voltha.KpiEvent2
+ var volthaEventSubCatgry voltha.EventSubCategory_Types
+
+ if portType == "NNIStats" {
+ volthaEventSubCatgry = voltha.EventSubCategory_NNI
+ } else {
+ volthaEventSubCatgry = voltha.EventSubCategory_PON
+ }
+
+ raisedTs := time.Now().UnixNano()
+ mmd := voltha.MetricMetaData{
+ Title: portType,
+ Ts: float64(raisedTs),
+ Context: context,
+ DeviceId: devID,
+ }
+
+ metricInfo.Metadata = &mmd
+ metricInfo.Metrics = val
+
+ ke.SliceData = []*voltha.MetricInformation{&metricInfo}
+ ke.Type = voltha.KpiEventType_slice
+ ke.Ts = float64(time.Now().UnixNano())
+
+ if err := StatMgr.Device.EventProxy.SendKpiEvent("STATS_EVENT", &ke, voltha.EventCategory_EQUIPMENT, volthaEventSubCatgry, raisedTs); err != nil {
+ log.Errorw("Failed to send Pon stats", log.Fields{"err": err})
+ }
+
+}
+
+// PortStatisticsIndication handles the port statistics indication
+func (StatMgr *OpenOltStatisticsMgr) PortStatisticsIndication(PortStats *openolt.PortStatistics, NumPonPorts uint32) {
+ log.Debugf("port-stats-collected %v", PortStats)
+ StatMgr.PortsStatisticsKpis(PortStats, NumPonPorts)
+ log.Infow("Received port stats indication", log.Fields{"PortStats": PortStats})
+ // TODO send stats to core topic to the voltha kafka or a different kafka ?
+}
+
+// FlowStatisticsIndication to be implemented
+func FlowStatisticsIndication(self, FlowStats *openolt.FlowStatistics) {
+ log.Debugf("flow-stats-collected %v", FlowStats)
+ //TODO send to kafka ?
+}
+
+// PortsStatisticsKpis map the port stats values into a dictionary, creates the kpiEvent and then publish to Kafka
+func (StatMgr *OpenOltStatisticsMgr) PortsStatisticsKpis(PortStats *openolt.PortStatistics, NumPonPorts uint32) {
+
+ /*map the port stats values into a dictionary
+ Create a kpoEvent and publish to Kafka
+
+ :param port_stats:
+ :return:
+ */
+ //var err error
+ IntfID := PortStats.IntfId
+
+ if (IntfIDToPortNo(1, voltha.Port_ETHERNET_NNI) < IntfID) &&
+ (IntfID < IntfIDToPortNo(4, voltha.Port_ETHERNET_NNI)) {
+ /*
+ for this release we are only interested in the first NNI for
+ Northbound.
+ we are not using the other 3
+ */
+ return
+ } else if IntfIDToPortNo(0, voltha.Port_ETHERNET_NNI) == IntfID {
+
+ var portNNIStat NniPort
+ portNNIStat.IntfID = IntfID
+ portNNIStat.PortNum = uint32(0)
+ portNNIStat.RxBytes = PortStats.RxBytes
+ portNNIStat.RxPackets = PortStats.RxPackets
+ portNNIStat.RxUcastPackets = PortStats.RxUcastPackets
+ portNNIStat.RxMcastPackets = PortStats.RxMcastPackets
+ portNNIStat.RxBcastPackets = PortStats.RxBcastPackets
+ portNNIStat.TxBytes = PortStats.TxBytes
+ portNNIStat.TxPackets = PortStats.TxPackets
+ portNNIStat.TxUcastPackets = PortStats.TxUcastPackets
+ portNNIStat.TxMcastPackets = PortStats.TxMcastPackets
+ portNNIStat.TxBcastPackets = PortStats.TxBcastPackets
+ mutex.Lock()
+ StatMgr.NorthBoundPort[0] = &portNNIStat
+ mutex.Unlock()
+ log.Debugf("Received-NNI-Stats: %v", StatMgr.NorthBoundPort)
+ }
+ for i := uint32(0); i < NumPonPorts; i++ {
+
+ if IntfIDToPortNo(i, voltha.Port_PON_OLT) == IntfID {
+ var portPonStat PonPort
+ portPonStat.IntfID = IntfID
+ portPonStat.PortNum = i
+ portPonStat.PONID = i
+ portPonStat.RxBytes = PortStats.RxBytes
+ portPonStat.RxPackets = PortStats.RxPackets
+ portPonStat.RxUcastPackets = PortStats.RxUcastPackets
+ portPonStat.RxMcastPackets = PortStats.RxMcastPackets
+ portPonStat.RxBcastPackets = PortStats.RxBcastPackets
+ portPonStat.TxBytes = PortStats.TxBytes
+ portPonStat.TxPackets = PortStats.TxPackets
+ portPonStat.TxUcastPackets = PortStats.TxUcastPackets
+ portPonStat.TxMcastPackets = PortStats.TxMcastPackets
+ portPonStat.TxBcastPackets = PortStats.TxBcastPackets
+ mutex.Lock()
+ StatMgr.SouthBoundPort[i] = &portPonStat
+ mutex.Unlock()
+ log.Debugf("Received-PON-Stats-for-Port %v : %v", i, StatMgr.SouthBoundPort[i])
+ }
+ }
+
+ /*
+ Based upon the intf_id map to an nni port or a pon port
+ the intf_id is the key to the north or south bound collections
+
+ Based upon the intf_id the port object (nni_port or pon_port) will
+ have its data attr. updated by the current dataset collected.
+
+ For prefixing the rule is currently to use the port number and not the intf_id
+ */
+ //FIXME : Just use first NNI for now
+ /* TODO should the data be marshaled before sending it ?
+ if IntfID == IntfIdToPortNo(0, voltha.Port_ETHERNET_NNI) {
+ //NNI port (just the first one)
+ err = UpdatePortObjectKpiData(StatMgr.NorthBoundPorts[PortStats.IntfID], PMData)
+ } else {
+ //PON ports
+ err = UpdatePortObjectKpiData(SouthboundPorts[PortStats.IntfID], PMData)
+ }
+ if (err != nil) {
+ log.Error("Error publishing statistics data")
+ }
+ */
+
+}
diff --git a/internal/pkg/core/statsmanager_test.go b/internal/pkg/core/statsmanager_test.go
new file mode 100644
index 0000000..6ea2487
--- /dev/null
+++ b/internal/pkg/core/statsmanager_test.go
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2019-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 core provides the utility for olt devices, flows and statistics
+package core
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "github.com/opencord/voltha-protos/v3/go/openolt"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+func init() {
+ _, _ = log.AddPackage(log.JSON, log.DebugLevel, nil)
+}
+func TestOpenOltStatisticsMgr_PortStatisticsIndication(t *testing.T) {
+ device := &voltha.Device{
+ Id: "olt",
+ Root: true,
+ ParentId: "logical_device",
+ Ports: []*voltha.Port{
+ {PortNo: 1, Label: "pon", Type: voltha.Port_ETHERNET_UNI},
+ {PortNo: 2, Label: "nni", Type: voltha.Port_ETHERNET_NNI},
+ },
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ DeviceId: "olt",
+ DeviceType: "onu",
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ },
+ ConnectStatus: 1,
+ }
+ dh := newMockDeviceHandler()
+ dh.device = device
+ StatMgr := NewOpenOltStatsMgr(dh)
+
+ type args struct {
+ PortStats *openolt.PortStatistics
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ // TODO: Add test cases.
+ {"PortStatisticsIndication", args{PortStats: &openolt.PortStatistics{}}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+
+ StatMgr.PortStatisticsIndication(tt.args.PortStats, 16)
+ })
+ }
+}
+
+func TestOpenOltStatisticsMgr_publishMetrics(t *testing.T) {
+ type fields struct {
+ Device *DeviceHandler
+ NorthBoundPort map[uint32]*NniPort
+ SouthBoundPort map[uint32]*PonPort
+ }
+ type args struct {
+ portType string
+ val map[string]float32
+ portnum uint32
+ context map[string]string
+ }
+ ctx := map[string]string{}
+ ctx["deviceID"] = "Test"
+ ponmap := map[uint32]*PonPort{}
+ ponmap[0] = &PonPort{
+ PONID: 0,
+ DeviceID: "onu1",
+ IntfID: 0,
+ PortNum: 0,
+ PortID: 0,
+ Label: "",
+ ONUs: nil,
+ ONUsByID: nil,
+ RxBytes: 0,
+ RxPackets: 0,
+ RxUcastPackets: 0,
+ RxMcastPackets: 0,
+ RxBcastPackets: 0,
+ RxErrorPackets: 0,
+ TxBytes: 0,
+ TxPackets: 0,
+ TxUcastPackets: 0,
+ TxMcastPackets: 0,
+ TxBcastPackets: 0,
+ TxErrorPackets: 0,
+ RxCrcErrors: 0,
+ BipErrors: 0,
+ }
+ nnimap := map[uint32]*NniPort{}
+ nnimap[0] = &NniPort{
+ PortNum: 0,
+ Name: "olt1",
+ LogicalPort: 0,
+ IntfID: 0,
+ RxBytes: 0,
+ RxPackets: 0,
+ RxUcastPackets: 0,
+ RxMcastPackets: uint64(1111),
+ RxBcastPackets: 0,
+ RxErrorPackets: 0,
+ TxBytes: 0,
+ TxPackets: 0,
+ TxUcastPackets: 0,
+ TxMcastPackets: 0,
+ TxBcastPackets: 0,
+ TxErrorPackets: 0,
+ RxCrcErrors: 0,
+ BipErrors: 0,
+ }
+ pval := make(map[string]float32)
+ pval["rx_bytes"] = float32(111)
+ nval := make(map[string]float32)
+ nval["rx_bytes"] = float32(111)
+ dhandlerNNI := newMockDeviceHandler()
+ dhandlerNNI.portStats = &OpenOltStatisticsMgr{Device: nil, SouthBoundPort: nil, NorthBoundPort: nnimap}
+ dhandlerPON := newMockDeviceHandler()
+ dhandlerPON.portStats = &OpenOltStatisticsMgr{Device: nil, SouthBoundPort: ponmap, NorthBoundPort: nil}
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ }{
+ {
+ name: "PublishNNIMetrics-1",
+ fields: fields{
+ Device: dhandlerNNI,
+ NorthBoundPort: nnimap,
+ SouthBoundPort: nil,
+ },
+ args: args{
+ portType: "NNIStats",
+ val: nval,
+ portnum: 0,
+ context: ctx,
+ },
+ },
+ {
+ name: "PublishPONMetrics-1",
+ fields: fields{
+ Device: dhandlerPON,
+ NorthBoundPort: nil,
+ SouthBoundPort: ponmap,
+ },
+ args: args{
+ portType: "PONStats",
+ val: pval,
+ portnum: 0,
+ context: ctx,
+ },
+ },
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ StatMgr := &OpenOltStatisticsMgr{
+ Device: tt.fields.Device,
+ NorthBoundPort: tt.fields.NorthBoundPort,
+ SouthBoundPort: tt.fields.SouthBoundPort,
+ }
+ StatMgr.publishMetrics(tt.args.portType, tt.args.val, tt.args.portnum, tt.args.context, "onu1")
+
+ })
+ }
+}
+
+func TestOpenOltStatisticsMgr_collectNNIMetrics(t *testing.T) {
+ type fields struct {
+ Device *DeviceHandler
+ NorthBoundPort map[uint32]*NniPort
+ SouthBoundPort map[uint32]*PonPort
+ }
+ type args struct {
+ nniID uint32
+ }
+ dhandler := newMockDeviceHandler()
+ pmconfig := make(map[string]*voltha.PmConfig)
+ pmconfig["rx_bytes"] = &voltha.PmConfig{Name: "olt"}
+
+ var res map[string]float32
+ nnimap := map[uint32]*NniPort{}
+ nnimap[0] = &NniPort{Name: "olt"}
+ nnimap[1] = &NniPort{Name: "olt"}
+ dh := &DeviceHandler{portStats: &OpenOltStatisticsMgr{Device: dhandler, SouthBoundPort: nil, NorthBoundPort: nnimap}}
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want map[string]float32
+ }{
+ {"CollectNNIMetrics-1", fields{
+ Device: dh,
+ NorthBoundPort: nnimap,
+ SouthBoundPort: nil,
+ }, args{0}, res},
+ {"CollectNNIMetrics-2", fields{
+ Device: dh,
+ NorthBoundPort: nnimap,
+ SouthBoundPort: nil,
+ }, args{1}, res},
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ StatMgr := &OpenOltStatisticsMgr{
+ Device: tt.fields.Device,
+ NorthBoundPort: tt.fields.NorthBoundPort,
+ SouthBoundPort: tt.fields.SouthBoundPort,
+ }
+ got := StatMgr.collectNNIMetrics(tt.args.nniID)
+ if reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("collectNNIMetrics() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltStatisticsMgr_collectPONMetrics(t *testing.T) {
+ type fields struct {
+ Device *DeviceHandler
+ NorthBoundPort map[uint32]*NniPort
+ SouthBoundPort map[uint32]*PonPort
+ }
+ type args struct {
+ pID uint32
+ }
+ dhandler := newMockDeviceHandler()
+ pmconfig := make(map[string]*voltha.PmConfig)
+ pmconfig["rx_bytes"] = &voltha.PmConfig{Name: "olt"}
+
+ var res map[string]float32
+ ponmap := map[uint32]*PonPort{}
+ ponmap[0] = &PonPort{DeviceID: "olt"}
+ ponmap[1] = &PonPort{DeviceID: "olt"}
+ dh := &DeviceHandler{portStats: &OpenOltStatisticsMgr{Device: dhandler, SouthBoundPort: ponmap, NorthBoundPort: nil}}
+
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want map[string]float32
+ }{
+ {"CollectPONMetrics-1", fields{
+ Device: dh,
+ NorthBoundPort: nil,
+ SouthBoundPort: ponmap,
+ }, args{0}, res},
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ StatMgr := &OpenOltStatisticsMgr{
+ Device: tt.fields.Device,
+ NorthBoundPort: tt.fields.NorthBoundPort,
+ SouthBoundPort: tt.fields.SouthBoundPort,
+ }
+ got := StatMgr.collectPONMetrics(tt.args.pID)
+ if reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("collectPONMetrics() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/internal/pkg/resourcemanager/resourcemanager.go b/internal/pkg/resourcemanager/resourcemanager.go
new file mode 100755
index 0000000..7dcdbb3
--- /dev/null
+++ b/internal/pkg/resourcemanager/resourcemanager.go
@@ -0,0 +1,1455 @@
+/*
+ * Copyright 2019-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 resourcemanager provides the utility for managing resources
+package resourcemanager
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "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"
+ ponrmgr "github.com/opencord/voltha-lib-go/v3/pkg/ponresourcemanager"
+ ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ "github.com/opencord/voltha-protos/v3/go/openolt"
+)
+
+const (
+ // KvstoreTimeout specifies the time out for KV Store Connection
+ KvstoreTimeout = 5
+ // BasePathKvStore - service/voltha/openolt/<device_id>
+ BasePathKvStore = "service/voltha/openolt/{%s}"
+ // TpIDPathSuffix - <(pon_id, onu_id, uni_id)>/tp_id
+ TpIDPathSuffix = "{%d,%d,%d}/tp_id"
+ //MeterIDPathSuffix - <(pon_id, onu_id, uni_id)>/<tp_id>/meter_id/<direction>
+ MeterIDPathSuffix = "{%d,%d,%d}/{%d}/meter_id/{%s}"
+ //NnniIntfID - nniintfids
+ NnniIntfID = "nniintfids"
+ // OnuPacketINPath path on the kvstore to store packetin gemport,which will be used for packetin, pcketout
+ //format: onu_packetin/<intfid>,<onuid>,<logicalport>
+ OnuPacketINPath = "onu_packetin/{%d,%d,%d}"
+ //FlowIDsForGem flowids_per_gem/<intfid>
+ FlowIDsForGem = "flowids_per_gem/{%d}"
+ //McastQueuesForIntf multicast queues for pon interfaces
+ McastQueuesForIntf = "mcast_qs_for_int"
+ //FlowGroup flow_groups/<flow_group_id>
+ // A group is stored under this path on the KV store after it has been installed to the device.
+ // It should also be deleted after it has been removed from the device accordingly.
+ FlowGroup = "flow_groups/{%d}"
+ //FlowGroupCached flow_groups_cached/<flow_group_id>
+ // When a group add request received, we create the group without setting any members to it since we cannot
+ // set any members to a group until it is associated with a multicast flow. It is a BAL limitation.
+ // When the related multicast flow has been created we perform set members operation for the group.
+ // That is why we need to keep the members of a group until the multicast flow creation request comes.
+ // We preserve the groups under "FlowGroupsCached" directory in the KV store temporarily. Having set members,
+ // we remove the group from the cached group store.
+ FlowGroupCached = "flow_groups_cached/{%d}"
+)
+
+// FlowInfo holds the flow information
+type FlowInfo struct {
+ Flow *openolt.Flow
+ FlowStoreCookie uint64
+ FlowCategory string
+ LogicalFlowID uint64
+}
+
+// OnuGemInfo holds onu information along with gem port list and uni port list
+type OnuGemInfo struct {
+ OnuID uint32
+ SerialNumber string
+ IntfID uint32
+ GemPorts []uint32
+ UniPorts []uint32
+}
+
+// PacketInInfoKey is the key for packet in gemport
+type PacketInInfoKey struct {
+ IntfID uint32
+ OnuID uint32
+ LogicalPort uint32
+}
+
+// GroupInfo holds group information
+type GroupInfo struct {
+ GroupID uint32
+ OutPorts []uint32
+}
+
+// OpenOltResourceMgr holds resource related information as provided below for each field
+type OpenOltResourceMgr struct {
+ DeviceID string // OLT device id
+ HostAndPort string // Host and port of the kv store to connect to
+ Args string // args
+ KVStore *db.Backend // backend kv store connection handle
+ DeviceType string
+ Host string // Host ip of the kv store
+ Port int // port of the kv store
+ DevInfo *openolt.DeviceInfo // device information
+ // array of pon resource managers per interface technology
+ ResourceMgrs map[uint32]*ponrmgr.PONResourceManager
+}
+
+func newKVClient(storeType string, address string, timeout uint32) (kvstore.Client, error) {
+ log.Infow("kv-store-type", log.Fields{"store": storeType})
+ switch storeType {
+ case "consul":
+ return kvstore.NewConsulClient(address, int(timeout))
+ case "etcd":
+ return kvstore.NewEtcdClient(address, int(timeout))
+ }
+ return nil, errors.New("unsupported-kv-store")
+}
+
+// SetKVClient sets the KV client and return a kv backend
+func SetKVClient(backend string, Host string, Port int, DeviceID string) *db.Backend {
+ addr := Host + ":" + strconv.Itoa(Port)
+ // TODO : Make sure direct call to NewBackend is working fine with backend , currently there is some
+ // issue between kv store and backend , core is not calling NewBackend directly
+ kvClient, err := newKVClient(backend, addr, KvstoreTimeout)
+ if err != nil {
+ log.Fatalw("Failed to init KV client\n", log.Fields{"err": err})
+ return nil
+ }
+ kvbackend := &db.Backend{
+ Client: kvClient,
+ StoreType: backend,
+ Host: Host,
+ Port: Port,
+ Timeout: KvstoreTimeout,
+ PathPrefix: fmt.Sprintf(BasePathKvStore, DeviceID)}
+
+ return kvbackend
+}
+
+// NewResourceMgr init a New resource manager instance which in turn instantiates pon resource manager
+// instances according to technology. Initializes the default resource ranges for all
+// the resources.
+func NewResourceMgr(ctx context.Context, deviceID string, KVStoreHostPort string, kvStoreType string, deviceType string, devInfo *openolt.DeviceInfo) *OpenOltResourceMgr {
+ var ResourceMgr OpenOltResourceMgr
+ log.Debugf("Init new resource manager , host_port: %s, deviceid: %s", KVStoreHostPort, deviceID)
+ ResourceMgr.HostAndPort = KVStoreHostPort
+ ResourceMgr.DeviceType = deviceType
+ ResourceMgr.DevInfo = devInfo
+ IPPort := strings.Split(KVStoreHostPort, ":")
+ ResourceMgr.Host = IPPort[0]
+ ResourceMgr.Port, _ = strconv.Atoi(IPPort[1])
+
+ Backend := kvStoreType
+ ResourceMgr.KVStore = SetKVClient(Backend, ResourceMgr.Host,
+ ResourceMgr.Port, deviceID)
+ if ResourceMgr.KVStore == nil {
+ log.Error("Failed to setup KV store")
+ }
+ Ranges := make(map[string]*openolt.DeviceInfo_DeviceResourceRanges)
+ RsrcMgrsByTech := make(map[string]*ponrmgr.PONResourceManager)
+ ResourceMgr.ResourceMgrs = make(map[uint32]*ponrmgr.PONResourceManager)
+
+ // TODO self.args = registry('main').get_args()
+
+ /*
+ If a legacy driver returns protobuf without any ranges,s synthesize one from
+ the legacy global per-device information. This, in theory, is temporary until
+ the legacy drivers are upgrade to support pool ranges.
+ */
+ if devInfo.Ranges == nil {
+ var ranges openolt.DeviceInfo_DeviceResourceRanges
+ ranges.Technology = devInfo.GetTechnology()
+
+ NumPONPorts := devInfo.GetPonPorts()
+ var index uint32
+ for index = 0; index < NumPONPorts; index++ {
+ ranges.IntfIds = append(ranges.IntfIds, index)
+ }
+
+ var Pool openolt.DeviceInfo_DeviceResourceRanges_Pool
+ Pool.Type = openolt.DeviceInfo_DeviceResourceRanges_Pool_ONU_ID
+ Pool.Start = devInfo.OnuIdStart
+ Pool.End = devInfo.OnuIdEnd
+ Pool.Sharing = openolt.DeviceInfo_DeviceResourceRanges_Pool_DEDICATED_PER_INTF
+ onuPool := Pool
+ ranges.Pools = append(ranges.Pools, &onuPool)
+
+ Pool.Type = openolt.DeviceInfo_DeviceResourceRanges_Pool_ALLOC_ID
+ Pool.Start = devInfo.AllocIdStart
+ Pool.End = devInfo.AllocIdEnd
+ Pool.Sharing = openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH
+ allocPool := Pool
+ ranges.Pools = append(ranges.Pools, &allocPool)
+
+ Pool.Type = openolt.DeviceInfo_DeviceResourceRanges_Pool_GEMPORT_ID
+ Pool.Start = devInfo.GemportIdStart
+ Pool.End = devInfo.GemportIdEnd
+ Pool.Sharing = openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH
+ gemPool := Pool
+ ranges.Pools = append(ranges.Pools, &gemPool)
+
+ Pool.Type = openolt.DeviceInfo_DeviceResourceRanges_Pool_FLOW_ID
+ Pool.Start = devInfo.FlowIdStart
+ Pool.End = devInfo.FlowIdEnd
+ Pool.Sharing = openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH
+ ranges.Pools = append(ranges.Pools, &Pool)
+ // Add to device info
+ devInfo.Ranges = append(devInfo.Ranges, &ranges)
+ }
+
+ // Create a separate Resource Manager instance for each range. This assumes that
+ // each technology is represented by only a single range
+ var GlobalPONRsrcMgr *ponrmgr.PONResourceManager
+ var err error
+ for _, TechRange := range devInfo.Ranges {
+ technology := TechRange.Technology
+ log.Debugf("Device info technology %s", technology)
+ Ranges[technology] = TechRange
+ RsrcMgrsByTech[technology], err = ponrmgr.NewPONResourceManager(technology, deviceType, deviceID,
+ Backend, ResourceMgr.Host, ResourceMgr.Port)
+ if err != nil {
+ log.Errorf("Failed to create pon resource manager instance for technology %s", technology)
+ return nil
+ }
+ // resource_mgrs_by_tech[technology] = resource_mgr
+ if GlobalPONRsrcMgr == nil {
+ GlobalPONRsrcMgr = RsrcMgrsByTech[technology]
+ }
+ for _, IntfID := range TechRange.IntfIds {
+ ResourceMgr.ResourceMgrs[uint32(IntfID)] = RsrcMgrsByTech[technology]
+ }
+ // self.initialize_device_resource_range_and_pool(resource_mgr, global_resource_mgr, arange)
+ InitializeDeviceResourceRangeAndPool(ctx, RsrcMgrsByTech[technology], GlobalPONRsrcMgr,
+ TechRange, devInfo)
+ }
+ // After we have initialized resource ranges, initialize the
+ // resource pools accordingly.
+ for _, PONRMgr := range RsrcMgrsByTech {
+ _ = PONRMgr.InitDeviceResourcePool(ctx)
+ }
+ log.Info("Initialization of resource manager success!")
+ return &ResourceMgr
+}
+
+// InitializeDeviceResourceRangeAndPool initializes the resource range pool according to the sharing type, then apply
+// device specific information. If KV doesn't exist
+// or is broader than the device, the device's information will
+// dictate the range limits
+func InitializeDeviceResourceRangeAndPool(ctx context.Context, ponRMgr *ponrmgr.PONResourceManager, globalPONRMgr *ponrmgr.PONResourceManager,
+ techRange *openolt.DeviceInfo_DeviceResourceRanges, devInfo *openolt.DeviceInfo) {
+
+ // init the resource range pool according to the sharing type
+
+ log.Debugf("Resource range pool init for technology %s", ponRMgr.Technology)
+ // first load from KV profiles
+ status := ponRMgr.InitResourceRangesFromKVStore(ctx)
+ if !status {
+ log.Debugf("Failed to load resource ranges from KV store for tech %s", ponRMgr.Technology)
+ }
+
+ /*
+ Then apply device specific information. If KV doesn't exist
+ or is broader than the device, the device's information will
+ dictate the range limits
+ */
+ log.Debugw("Using device info to init pon resource ranges", log.Fields{"Tech": ponRMgr.Technology})
+
+ ONUIDStart := devInfo.OnuIdStart
+ ONUIDEnd := devInfo.OnuIdEnd
+ ONUIDShared := openolt.DeviceInfo_DeviceResourceRanges_Pool_DEDICATED_PER_INTF
+ ONUIDSharedPoolID := uint32(0)
+ AllocIDStart := devInfo.AllocIdStart
+ AllocIDEnd := devInfo.AllocIdEnd
+ AllocIDShared := openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH // TODO EdgeCore/BAL limitation
+ AllocIDSharedPoolID := uint32(0)
+ GEMPortIDStart := devInfo.GemportIdStart
+ GEMPortIDEnd := devInfo.GemportIdEnd
+ GEMPortIDShared := openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH // TODO EdgeCore/BAL limitation
+ GEMPortIDSharedPoolID := uint32(0)
+ FlowIDStart := devInfo.FlowIdStart
+ FlowIDEnd := devInfo.FlowIdEnd
+ FlowIDShared := openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH // TODO EdgeCore/BAL limitation
+ FlowIDSharedPoolID := uint32(0)
+
+ var FirstIntfPoolID uint32
+ var SharedPoolID uint32
+
+ /*
+ * As a zero check is made against SharedPoolID to check whether the resources are shared across all intfs
+ * if resources are shared across interfaces then SharedPoolID is given a positive number.
+ */
+ for _, FirstIntfPoolID = range techRange.IntfIds {
+ // skip the intf id 0
+ if FirstIntfPoolID == 0 {
+ continue
+ }
+ break
+ }
+
+ for _, RangePool := range techRange.Pools {
+ if RangePool.Sharing == openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH {
+ SharedPoolID = FirstIntfPoolID
+ } else if RangePool.Sharing == openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_SAME_TECH {
+ SharedPoolID = FirstIntfPoolID
+ } else {
+ SharedPoolID = 0
+ }
+ if RangePool.Type == openolt.DeviceInfo_DeviceResourceRanges_Pool_ONU_ID {
+ ONUIDStart = RangePool.Start
+ ONUIDEnd = RangePool.End
+ ONUIDShared = RangePool.Sharing
+ ONUIDSharedPoolID = SharedPoolID
+ } else if RangePool.Type == openolt.DeviceInfo_DeviceResourceRanges_Pool_ALLOC_ID {
+ AllocIDStart = RangePool.Start
+ AllocIDEnd = RangePool.End
+ AllocIDShared = RangePool.Sharing
+ AllocIDSharedPoolID = SharedPoolID
+ } else if RangePool.Type == openolt.DeviceInfo_DeviceResourceRanges_Pool_GEMPORT_ID {
+ GEMPortIDStart = RangePool.Start
+ GEMPortIDEnd = RangePool.End
+ GEMPortIDShared = RangePool.Sharing
+ GEMPortIDSharedPoolID = SharedPoolID
+ } else if RangePool.Type == openolt.DeviceInfo_DeviceResourceRanges_Pool_FLOW_ID {
+ FlowIDStart = RangePool.Start
+ FlowIDEnd = RangePool.End
+ FlowIDShared = RangePool.Sharing
+ FlowIDSharedPoolID = SharedPoolID
+ }
+ }
+
+ log.Debugw("Device info init", log.Fields{"technology": techRange.Technology,
+ "onu_id_start": ONUIDStart, "onu_id_end": ONUIDEnd, "onu_id_shared_pool_id": ONUIDSharedPoolID,
+ "alloc_id_start": AllocIDStart, "alloc_id_end": AllocIDEnd,
+ "alloc_id_shared_pool_id": AllocIDSharedPoolID,
+ "gemport_id_start": GEMPortIDStart, "gemport_id_end": GEMPortIDEnd,
+ "gemport_id_shared_pool_id": GEMPortIDSharedPoolID,
+ "flow_id_start": FlowIDStart,
+ "flow_id_end_idx": FlowIDEnd,
+ "flow_id_shared_pool_id": FlowIDSharedPoolID,
+ "intf_ids": techRange.IntfIds,
+ "uni_id_start": 0,
+ "uni_id_end_idx": 1, /*MaxUNIIDperONU()*/
+ })
+
+ ponRMgr.InitDefaultPONResourceRanges(ONUIDStart, ONUIDEnd, ONUIDSharedPoolID,
+ AllocIDStart, AllocIDEnd, AllocIDSharedPoolID,
+ GEMPortIDStart, GEMPortIDEnd, GEMPortIDSharedPoolID,
+ FlowIDStart, FlowIDEnd, FlowIDSharedPoolID, 0, 1,
+ devInfo.PonPorts, techRange.IntfIds)
+
+ // For global sharing, make sure to refresh both local and global resource manager instances' range
+
+ if ONUIDShared == openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH {
+ globalPONRMgr.UpdateRanges(ponrmgr.ONU_ID_START_IDX, ONUIDStart, ponrmgr.ONU_ID_END_IDX, ONUIDEnd,
+ "", 0, nil)
+ ponRMgr.UpdateRanges(ponrmgr.ONU_ID_START_IDX, ONUIDStart, ponrmgr.ONU_ID_END_IDX, ONUIDEnd,
+ "", 0, globalPONRMgr)
+ }
+ if AllocIDShared == openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH {
+ globalPONRMgr.UpdateRanges(ponrmgr.ALLOC_ID_START_IDX, AllocIDStart, ponrmgr.ALLOC_ID_END_IDX, AllocIDEnd,
+ "", 0, nil)
+
+ ponRMgr.UpdateRanges(ponrmgr.ALLOC_ID_START_IDX, AllocIDStart, ponrmgr.ALLOC_ID_END_IDX, AllocIDEnd,
+ "", 0, globalPONRMgr)
+ }
+ if GEMPortIDShared == openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH {
+ globalPONRMgr.UpdateRanges(ponrmgr.GEMPORT_ID_START_IDX, GEMPortIDStart, ponrmgr.GEMPORT_ID_END_IDX, GEMPortIDEnd,
+ "", 0, nil)
+ ponRMgr.UpdateRanges(ponrmgr.GEMPORT_ID_START_IDX, GEMPortIDStart, ponrmgr.GEMPORT_ID_END_IDX, GEMPortIDEnd,
+ "", 0, globalPONRMgr)
+ }
+ if FlowIDShared == openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH {
+ globalPONRMgr.UpdateRanges(ponrmgr.FLOW_ID_START_IDX, FlowIDStart, ponrmgr.FLOW_ID_END_IDX, FlowIDEnd,
+ "", 0, nil)
+ ponRMgr.UpdateRanges(ponrmgr.FLOW_ID_START_IDX, FlowIDStart, ponrmgr.FLOW_ID_END_IDX, FlowIDEnd,
+ "", 0, globalPONRMgr)
+ }
+
+ // Make sure loaded range fits the platform bit encoding ranges
+ ponRMgr.UpdateRanges(ponrmgr.UNI_ID_START_IDX, 0, ponrmgr.UNI_ID_END_IDX /* TODO =OpenOltPlatform.MAX_UNIS_PER_ONU-1*/, 1, "", 0, nil)
+}
+
+// Delete clears used resources for the particular olt device being deleted
+func (RsrcMgr *OpenOltResourceMgr) Delete(ctx context.Context) error {
+ /* TODO
+ def __del__(self):
+ self.log.info("clearing-device-resource-pool")
+ for key, resource_mgr in self.resource_mgrs.iteritems():
+ resource_mgr.clear_device_resource_pool()
+
+ def assert_pon_id_limit(self, pon_intf_id):
+ assert pon_intf_id in self.resource_mgrs
+
+ def assert_onu_id_limit(self, pon_intf_id, onu_id):
+ self.assert_pon_id_limit(pon_intf_id)
+ self.resource_mgrs[pon_intf_id].assert_resource_limits(onu_id, PONResourceManager.ONU_ID)
+
+ @property
+ def max_uni_id_per_onu(self):
+ return 0 #OpenOltPlatform.MAX_UNIS_PER_ONU-1, zero-based indexing Uncomment or override to make default multi-uni
+
+ def assert_uni_id_limit(self, pon_intf_id, onu_id, uni_id):
+ self.assert_onu_id_limit(pon_intf_id, onu_id)
+ self.resource_mgrs[pon_intf_id].assert_resource_limits(uni_id, PONResourceManager.UNI_ID)
+ */
+ for _, rsrcMgr := range RsrcMgr.ResourceMgrs {
+ if err := rsrcMgr.ClearDeviceResourcePool(ctx); err != nil {
+ log.Debug("Failed to clear device resource pool")
+ return err
+ }
+ }
+ log.Debug("Cleared device resource pool")
+ return nil
+}
+
+// GetONUID returns the available OnuID for the given pon-port
+func (RsrcMgr *OpenOltResourceMgr) GetONUID(ctx context.Context, ponIntfID uint32) (uint32, error) {
+ // Check if Pon Interface ID is present in Resource-manager-map
+ if _, ok := RsrcMgr.ResourceMgrs[ponIntfID]; !ok {
+ err := errors.New("invalid-pon-interface-" + strconv.Itoa(int(ponIntfID)))
+ return 0, err
+ }
+ // Get ONU id for a provided pon interface ID.
+ ONUID, err := RsrcMgr.ResourceMgrs[ponIntfID].GetResourceID(ctx, ponIntfID,
+ ponrmgr.ONU_ID, 1)
+ if err != nil {
+ log.Errorf("Failed to get resource for interface %d for type %s",
+ ponIntfID, ponrmgr.ONU_ID)
+ return 0, err
+ }
+ if ONUID != nil {
+ RsrcMgr.ResourceMgrs[ponIntfID].InitResourceMap(ctx, fmt.Sprintf("%d,%d", ponIntfID, ONUID[0]))
+ return ONUID[0], err
+ }
+
+ return 0, err // return OnuID 0 on error
+}
+
+// GetFlowIDInfo returns the slice of flow info of the given pon-port
+// Note: For flows which trap from the NNI and not really associated with any particular
+// ONU (like LLDP), the onu_id and uni_id is set as -1. The intf_id is the NNI intf_id.
+func (RsrcMgr *OpenOltResourceMgr) GetFlowIDInfo(ctx context.Context, ponIntfID uint32, onuID int32, uniID int32, flowID uint32) *[]FlowInfo {
+ var flows []FlowInfo
+
+ FlowPath := fmt.Sprintf("%d,%d,%d", ponIntfID, onuID, uniID)
+ if err := RsrcMgr.ResourceMgrs[ponIntfID].GetFlowIDInfo(ctx, FlowPath, flowID, &flows); err != nil {
+ log.Errorw("Error while getting flows from KV store", log.Fields{"flowId": flowID})
+ return nil
+ }
+ if len(flows) == 0 {
+ log.Debugw("No flowInfo found in KV store", log.Fields{"flowPath": FlowPath})
+ return nil
+ }
+ return &flows
+}
+
+// GetCurrentFlowIDsForOnu fetches flow ID from the resource manager
+// Note: For flows which trap from the NNI and not really associated with any particular
+// ONU (like LLDP), the onu_id and uni_id is set as -1. The intf_id is the NNI intf_id.
+func (RsrcMgr *OpenOltResourceMgr) GetCurrentFlowIDsForOnu(ctx context.Context, PONIntfID uint32, ONUID int32, UNIID int32) []uint32 {
+
+ FlowPath := fmt.Sprintf("%d,%d,%d", PONIntfID, ONUID, UNIID)
+ if mgrs, exist := RsrcMgr.ResourceMgrs[PONIntfID]; exist {
+ return mgrs.GetCurrentFlowIDsForOnu(ctx, FlowPath)
+ }
+ return nil
+}
+
+// UpdateFlowIDInfo updates flow info for the given pon interface, onu id, and uni id
+// Note: For flows which trap from the NNI and not really associated with any particular
+// ONU (like LLDP), the onu_id and uni_id is set as -1. The intf_id is the NNI intf_id.
+func (RsrcMgr *OpenOltResourceMgr) UpdateFlowIDInfo(ctx context.Context, ponIntfID int32, onuID int32, uniID int32,
+ flowID uint32, flowData *[]FlowInfo) error {
+ FlowPath := fmt.Sprintf("%d,%d,%d", ponIntfID, onuID, uniID)
+ return RsrcMgr.ResourceMgrs[uint32(ponIntfID)].UpdateFlowIDInfoForOnu(ctx, FlowPath, flowID, *flowData)
+}
+
+// GetFlowID return flow ID for a given pon interface id, onu id and uni id
+func (RsrcMgr *OpenOltResourceMgr) GetFlowID(ctx context.Context, ponIntfID uint32, ONUID int32, uniID int32,
+ gemportID uint32,
+ flowStoreCookie uint64,
+ flowCategory string, vlanPcp ...uint32) (uint32, error) {
+
+ var err error
+ FlowPath := fmt.Sprintf("%d,%d,%d", ponIntfID, ONUID, uniID)
+ FlowIDs := RsrcMgr.ResourceMgrs[ponIntfID].GetCurrentFlowIDsForOnu(ctx, FlowPath)
+ if FlowIDs != nil {
+ log.Debugw("Found flowId(s) for this ONU", log.Fields{"pon": ponIntfID, "ONUID": ONUID, "uniID": uniID, "KVpath": FlowPath})
+ for _, flowID := range FlowIDs {
+ FlowInfo := RsrcMgr.GetFlowIDInfo(ctx, ponIntfID, int32(ONUID), int32(uniID), uint32(flowID))
+ er := getFlowIDFromFlowInfo(FlowInfo, flowID, gemportID, flowStoreCookie, flowCategory, vlanPcp...)
+ if er == nil {
+ return flowID, er
+ }
+ }
+ }
+ log.Debug("No matching flows with flow cookie or flow category, allocating new flowid")
+ FlowIDs, err = RsrcMgr.ResourceMgrs[ponIntfID].GetResourceID(ctx, ponIntfID,
+ ponrmgr.FLOW_ID, 1)
+ if err != nil {
+ log.Errorf("Failed to get resource for interface %d for type %s",
+ ponIntfID, ponrmgr.FLOW_ID)
+ return uint32(0), err
+ }
+ if FlowIDs != nil {
+ _ = RsrcMgr.ResourceMgrs[ponIntfID].UpdateFlowIDForOnu(ctx, FlowPath, FlowIDs[0], true)
+ return FlowIDs[0], err
+ }
+
+ return 0, err
+}
+
+// GetAllocID return the first Alloc ID for a given pon interface id and onu id and then update the resource map on
+// the KV store with the list of alloc_ids allocated for the pon_intf_onu_id tuple
+// Currently of all the alloc_ids available, it returns the first alloc_id in the list for tha given ONU
+func (RsrcMgr *OpenOltResourceMgr) GetAllocID(ctx context.Context, intfID uint32, onuID uint32, uniID uint32) uint32 {
+
+ var err error
+ IntfOnuIDUniID := fmt.Sprintf("%d,%d,%d", intfID, onuID, uniID)
+ AllocID := RsrcMgr.ResourceMgrs[intfID].GetCurrentAllocIDForOnu(ctx, IntfOnuIDUniID)
+ if AllocID != nil {
+ // Since we support only one alloc_id for the ONU at the moment,
+ // return the first alloc_id in the list, if available, for that
+ // ONU.
+ log.Debugw("Retrieved alloc ID from pon resource mgr", log.Fields{"AllocID": AllocID})
+ return AllocID[0]
+ }
+ AllocID, err = RsrcMgr.ResourceMgrs[intfID].GetResourceID(ctx, intfID,
+ ponrmgr.ALLOC_ID, 1)
+
+ if AllocID == nil || err != nil {
+ log.Error("Failed to allocate alloc id")
+ return 0
+ }
+ // update the resource map on KV store with the list of alloc_id
+ // allocated for the pon_intf_onu_id tuple
+ err = RsrcMgr.ResourceMgrs[intfID].UpdateAllocIdsForOnu(ctx, IntfOnuIDUniID, AllocID)
+ if err != nil {
+ log.Error("Failed to update Alloc ID")
+ return 0
+ }
+ log.Debugw("Allocated new Tcont from pon resource mgr", log.Fields{"AllocID": AllocID})
+ return AllocID[0]
+}
+
+// UpdateAllocIdsForOnu updates alloc ids in kv store for a given pon interface id, onu id and uni id
+func (RsrcMgr *OpenOltResourceMgr) UpdateAllocIdsForOnu(ctx context.Context, ponPort uint32, onuID uint32, uniID uint32, allocID []uint32) error {
+
+ IntfOnuIDUniID := fmt.Sprintf("%d,%d,%d", ponPort, onuID, uniID)
+ return RsrcMgr.ResourceMgrs[ponPort].UpdateAllocIdsForOnu(ctx, IntfOnuIDUniID,
+ allocID)
+}
+
+// GetCurrentGEMPortIDsForOnu returns gem ports for given pon interface , onu id and uni id
+func (RsrcMgr *OpenOltResourceMgr) GetCurrentGEMPortIDsForOnu(ctx context.Context, intfID uint32, onuID uint32,
+ uniID uint32) []uint32 {
+
+ /* Get gem ports for given pon interface , onu id and uni id. */
+
+ IntfOnuIDUniID := fmt.Sprintf("%d,%d,%d", intfID, onuID, uniID)
+ return RsrcMgr.ResourceMgrs[intfID].GetCurrentGEMPortIDsForOnu(ctx, IntfOnuIDUniID)
+}
+
+// GetCurrentAllocIDsForOnu returns alloc ids for given pon interface and onu id
+func (RsrcMgr *OpenOltResourceMgr) GetCurrentAllocIDsForOnu(ctx context.Context, intfID uint32, onuID uint32, uniID uint32) []uint32 {
+
+ IntfOnuIDUniID := fmt.Sprintf("%d,%d,%d", intfID, onuID, uniID)
+ AllocID := RsrcMgr.ResourceMgrs[intfID].GetCurrentAllocIDForOnu(ctx, IntfOnuIDUniID)
+ if AllocID != nil {
+ return AllocID
+ }
+ return []uint32{}
+}
+
+// RemoveAllocIDForOnu removes the alloc id for given pon interface, onu id, uni id and alloc id
+func (RsrcMgr *OpenOltResourceMgr) RemoveAllocIDForOnu(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, allocID uint32) {
+ allocIDs := RsrcMgr.GetCurrentAllocIDsForOnu(ctx, intfID, onuID, uniID)
+ for i := 0; i < len(allocIDs); i++ {
+ if allocIDs[i] == allocID {
+ allocIDs = append(allocIDs[:i], allocIDs[i+1:]...)
+ break
+ }
+ }
+ err := RsrcMgr.UpdateAllocIdsForOnu(ctx, intfID, onuID, uniID, allocIDs)
+ if err != nil {
+ log.Errorf("Failed to Remove Alloc Id For Onu. IntfID %d onuID %d uniID %d allocID %d",
+ intfID, onuID, uniID, allocID)
+ }
+}
+
+// RemoveGemPortIDForOnu removes the gem port id for given pon interface, onu id, uni id and gem port id
+func (RsrcMgr *OpenOltResourceMgr) RemoveGemPortIDForOnu(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, gemPortID uint32) {
+ gemPortIDs := RsrcMgr.GetCurrentGEMPortIDsForOnu(ctx, intfID, onuID, uniID)
+ for i := 0; i < len(gemPortIDs); i++ {
+ if gemPortIDs[i] == gemPortID {
+ gemPortIDs = append(gemPortIDs[:i], gemPortIDs[i+1:]...)
+ break
+ }
+ }
+ err := RsrcMgr.UpdateGEMPortIDsForOnu(ctx, intfID, onuID, uniID, gemPortIDs)
+ if err != nil {
+ log.Errorf("Failed to Remove Gem Id For Onu. IntfID %d onuID %d uniID %d gemPortId %d",
+ intfID, onuID, uniID, gemPortID)
+ }
+}
+
+// UpdateGEMportsPonportToOnuMapOnKVStore updates onu and uni id associated with the gem port to the kv store
+// This stored information is used when packet_indication is received and we need to derive the ONU Id for which
+// the packet arrived based on the pon_intf and gemport available in the packet_indication
+func (RsrcMgr *OpenOltResourceMgr) UpdateGEMportsPonportToOnuMapOnKVStore(ctx context.Context, gemPorts []uint32, PonPort uint32,
+ onuID uint32, uniID uint32) error {
+
+ /* Update onu and uni id associated with the gem port to the kv store. */
+ var IntfGEMPortPath string
+ Data := fmt.Sprintf("%d %d", onuID, uniID)
+ for _, GEM := range gemPorts {
+ IntfGEMPortPath = fmt.Sprintf("%d,%d", PonPort, GEM)
+ Val, err := json.Marshal(Data)
+ if err != nil {
+ log.Error("failed to Marshal")
+ return err
+ }
+
+ if err = RsrcMgr.KVStore.Put(ctx, IntfGEMPortPath, Val); err != nil {
+ log.Errorf("Failed to update resource %s", IntfGEMPortPath)
+ return err
+ }
+ }
+ return nil
+}
+
+// RemoveGEMportPonportToOnuMapOnKVStore removes the relationship between the gem port and pon port
+func (RsrcMgr *OpenOltResourceMgr) RemoveGEMportPonportToOnuMapOnKVStore(ctx context.Context, GemPort uint32, PonPort uint32) {
+ IntfGEMPortPath := fmt.Sprintf("%d,%d", PonPort, GemPort)
+ err := RsrcMgr.KVStore.Delete(ctx, IntfGEMPortPath)
+ if err != nil {
+ log.Errorf("Failed to Remove Gem port-Pon port to onu map on kv store. Gem %d PonPort %d", GemPort, PonPort)
+ }
+}
+
+// GetGEMPortID gets gem port id for a particular pon port, onu id and uni id and then update the resource map on
+// the KV store with the list of gemport_id allocated for the pon_intf_onu_id tuple
+func (RsrcMgr *OpenOltResourceMgr) GetGEMPortID(ctx context.Context, ponPort uint32, onuID uint32,
+ uniID uint32, NumOfPorts uint32) ([]uint32, error) {
+
+ /* Get gem port id for a particular pon port, onu id
+ and uni id.
+ */
+
+ var err error
+ IntfOnuIDUniID := fmt.Sprintf("%d,%d,%d", ponPort, onuID, uniID)
+
+ GEMPortList := RsrcMgr.ResourceMgrs[ponPort].GetCurrentGEMPortIDsForOnu(ctx, IntfOnuIDUniID)
+ if GEMPortList != nil {
+ return GEMPortList, nil
+ }
+
+ GEMPortList, err = RsrcMgr.ResourceMgrs[ponPort].GetResourceID(ctx, ponPort,
+ ponrmgr.GEMPORT_ID, NumOfPorts)
+ if err != nil && GEMPortList == nil {
+ log.Errorf("Failed to get gem port id for %s", IntfOnuIDUniID)
+ return nil, err
+ }
+
+ // update the resource map on KV store with the list of gemport_id
+ // allocated for the pon_intf_onu_id tuple
+ err = RsrcMgr.ResourceMgrs[ponPort].UpdateGEMPortIDsForOnu(ctx, IntfOnuIDUniID,
+ GEMPortList)
+ if err != nil {
+ log.Errorf("Failed to update GEM ports to kv store for %s", IntfOnuIDUniID)
+ return nil, err
+ }
+ _ = RsrcMgr.UpdateGEMportsPonportToOnuMapOnKVStore(ctx, GEMPortList, ponPort,
+ onuID, uniID)
+ return GEMPortList, err
+}
+
+// UpdateGEMPortIDsForOnu updates gemport ids on to the kv store for a given pon port, onu id and uni id
+func (RsrcMgr *OpenOltResourceMgr) UpdateGEMPortIDsForOnu(ctx context.Context, ponPort uint32, onuID uint32,
+ uniID uint32, GEMPortList []uint32) error {
+ IntfOnuIDUniID := fmt.Sprintf("%d,%d,%d", ponPort, onuID, uniID)
+ return RsrcMgr.ResourceMgrs[ponPort].UpdateGEMPortIDsForOnu(ctx, IntfOnuIDUniID,
+ GEMPortList)
+
+}
+
+// FreeonuID releases(make free) onu id for a particular pon-port
+func (RsrcMgr *OpenOltResourceMgr) FreeonuID(ctx context.Context, intfID uint32, onuID []uint32) {
+
+ RsrcMgr.ResourceMgrs[intfID].FreeResourceID(ctx, intfID, ponrmgr.ONU_ID, onuID)
+
+ /* Free onu id for a particular interface.*/
+ var IntfonuID string
+ for _, onu := range onuID {
+ IntfonuID = fmt.Sprintf("%d,%d", intfID, onu)
+ RsrcMgr.ResourceMgrs[intfID].RemoveResourceMap(ctx, IntfonuID)
+ }
+}
+
+// FreeFlowID returns the free flow id for a given interface, onu id and uni id
+func (RsrcMgr *OpenOltResourceMgr) FreeFlowID(ctx context.Context, IntfID uint32, onuID int32,
+ uniID int32, FlowID uint32) {
+ var IntfONUID string
+ var err error
+ FlowIds := make([]uint32, 0)
+
+ FlowIds = append(FlowIds, FlowID)
+ IntfONUID = fmt.Sprintf("%d,%d,%d", IntfID, onuID, uniID)
+ err = RsrcMgr.ResourceMgrs[IntfID].UpdateFlowIDForOnu(ctx, IntfONUID, FlowID, false)
+ if err != nil {
+ log.Errorw("Failed to Update flow id for", log.Fields{"intf": IntfONUID})
+ }
+ RsrcMgr.ResourceMgrs[IntfID].RemoveFlowIDInfo(ctx, IntfONUID, FlowID)
+ RsrcMgr.ResourceMgrs[IntfID].FreeResourceID(ctx, IntfID, ponrmgr.FLOW_ID, FlowIds)
+}
+
+// FreeFlowIDs releases the flow Ids
+func (RsrcMgr *OpenOltResourceMgr) FreeFlowIDs(ctx context.Context, IntfID uint32, onuID uint32,
+ uniID uint32, FlowID []uint32) {
+
+ RsrcMgr.ResourceMgrs[IntfID].FreeResourceID(ctx, IntfID, ponrmgr.FLOW_ID, FlowID)
+
+ var IntfOnuIDUniID string
+ var err error
+ for _, flow := range FlowID {
+ IntfOnuIDUniID = fmt.Sprintf("%d,%d,%d", IntfID, onuID, uniID)
+ err = RsrcMgr.ResourceMgrs[IntfID].UpdateFlowIDForOnu(ctx, IntfOnuIDUniID, flow, false)
+ if err != nil {
+ log.Errorw("Failed to Update flow id for", log.Fields{"intf": IntfOnuIDUniID})
+ }
+ RsrcMgr.ResourceMgrs[IntfID].RemoveFlowIDInfo(ctx, IntfOnuIDUniID, flow)
+ }
+}
+
+// FreeAllocID frees AllocID on the PON resource pool and also frees the allocID association
+// for the given OLT device.
+func (RsrcMgr *OpenOltResourceMgr) FreeAllocID(ctx context.Context, IntfID uint32, onuID uint32,
+ uniID uint32, allocID uint32) {
+ RsrcMgr.RemoveAllocIDForOnu(ctx, IntfID, onuID, uniID, allocID)
+ allocIDs := make([]uint32, 0)
+ allocIDs = append(allocIDs, allocID)
+ RsrcMgr.ResourceMgrs[IntfID].FreeResourceID(ctx, IntfID, ponrmgr.ALLOC_ID, allocIDs)
+}
+
+// FreeGemPortID frees GemPortID on the PON resource pool and also frees the gemPortID association
+// for the given OLT device.
+func (RsrcMgr *OpenOltResourceMgr) FreeGemPortID(ctx context.Context, IntfID uint32, onuID uint32,
+ uniID uint32, gemPortID uint32) {
+ RsrcMgr.RemoveGemPortIDForOnu(ctx, IntfID, onuID, uniID, gemPortID)
+ gemPortIDs := make([]uint32, 0)
+ gemPortIDs = append(gemPortIDs, gemPortID)
+ RsrcMgr.ResourceMgrs[IntfID].FreeResourceID(ctx, IntfID, ponrmgr.GEMPORT_ID, gemPortIDs)
+}
+
+// FreePONResourcesForONU make the pon resources free for a given pon interface and onu id, and the clears the
+// resource map and the onuID associated with (pon_intf_id, gemport_id) tuple,
+func (RsrcMgr *OpenOltResourceMgr) FreePONResourcesForONU(ctx context.Context, intfID uint32, onuID uint32, uniID uint32) {
+
+ IntfOnuIDUniID := fmt.Sprintf("%d,%d,%d", intfID, onuID, uniID)
+
+ AllocIDs := RsrcMgr.ResourceMgrs[intfID].GetCurrentAllocIDForOnu(ctx, IntfOnuIDUniID)
+
+ RsrcMgr.ResourceMgrs[intfID].FreeResourceID(ctx, intfID,
+ ponrmgr.ALLOC_ID,
+ AllocIDs)
+
+ GEMPortIDs := RsrcMgr.ResourceMgrs[intfID].GetCurrentGEMPortIDsForOnu(ctx, IntfOnuIDUniID)
+ RsrcMgr.ResourceMgrs[intfID].FreeResourceID(ctx, intfID,
+ ponrmgr.GEMPORT_ID,
+ GEMPortIDs)
+
+ FlowIDs := RsrcMgr.ResourceMgrs[intfID].GetCurrentFlowIDsForOnu(ctx, IntfOnuIDUniID)
+ RsrcMgr.ResourceMgrs[intfID].FreeResourceID(ctx, intfID,
+ ponrmgr.FLOW_ID,
+ FlowIDs)
+ // Clear resource map associated with (pon_intf_id, gemport_id) tuple.
+ RsrcMgr.ResourceMgrs[intfID].RemoveResourceMap(ctx, IntfOnuIDUniID)
+ // Clear the ONU Id associated with the (pon_intf_id, gemport_id) tuple.
+ for _, GEM := range GEMPortIDs {
+ _ = RsrcMgr.KVStore.Delete(ctx, fmt.Sprintf("%d,%d", intfID, GEM))
+ }
+}
+
+// IsFlowCookieOnKVStore checks if the given flow cookie is present on the kv store
+// Returns true if the flow cookie is found, otherwise it returns false
+func (RsrcMgr *OpenOltResourceMgr) IsFlowCookieOnKVStore(ctx context.Context, ponIntfID uint32, onuID int32, uniID int32,
+ flowStoreCookie uint64) bool {
+
+ FlowPath := fmt.Sprintf("%d,%d,%d", ponIntfID, onuID, uniID)
+ FlowIDs := RsrcMgr.ResourceMgrs[ponIntfID].GetCurrentFlowIDsForOnu(ctx, FlowPath)
+ if FlowIDs != nil {
+ log.Debugw("Found flowId(s) for this ONU", log.Fields{"pon": ponIntfID, "onuID": onuID, "uniID": uniID, "KVpath": FlowPath})
+ for _, flowID := range FlowIDs {
+ FlowInfo := RsrcMgr.GetFlowIDInfo(ctx, ponIntfID, int32(onuID), int32(uniID), uint32(flowID))
+ if FlowInfo != nil {
+ log.Debugw("Found flows", log.Fields{"flows": *FlowInfo, "flowId": flowID})
+ for _, Info := range *FlowInfo {
+ if Info.FlowStoreCookie == flowStoreCookie {
+ log.Debug("Found flow matching with flowStore cookie", log.Fields{"flowId": flowID, "flowStoreCookie": flowStoreCookie})
+ return true
+ }
+ }
+ }
+ }
+ }
+ return false
+}
+
+// GetTechProfileIDForOnu fetches Tech-Profile-ID from the KV-Store for the given onu based on the path
+// This path is formed as the following: {IntfID, OnuID, UniID}/tp_id
+func (RsrcMgr *OpenOltResourceMgr) GetTechProfileIDForOnu(ctx context.Context, IntfID uint32, OnuID uint32, UniID uint32) []uint32 {
+ Path := fmt.Sprintf(TpIDPathSuffix, IntfID, OnuID, UniID)
+ var Data []uint32
+ Value, err := RsrcMgr.KVStore.Get(ctx, Path)
+ if err == nil {
+ if Value != nil {
+ Val, err := kvstore.ToByte(Value.Value)
+ if err != nil {
+ log.Errorw("Failed to convert into byte array", log.Fields{"error": err})
+ return Data
+ }
+ if err = json.Unmarshal(Val, &Data); err != nil {
+ log.Error("Failed to unmarshal", log.Fields{"error": err})
+ return Data
+ }
+ }
+ } else {
+ log.Errorf("Failed to get TP id from kvstore for path %s", Path)
+ }
+ log.Debugf("Getting TP id %d from path %s", Data, Path)
+ return Data
+
+}
+
+// RemoveTechProfileIDsForOnu deletes all tech profile ids from the KV-Store for the given onu based on the path
+// This path is formed as the following: {IntfID, OnuID, UniID}/tp_id
+func (RsrcMgr *OpenOltResourceMgr) RemoveTechProfileIDsForOnu(ctx context.Context, IntfID uint32, OnuID uint32, UniID uint32) error {
+ IntfOnuUniID := fmt.Sprintf(TpIDPathSuffix, IntfID, OnuID, UniID)
+ if err := RsrcMgr.KVStore.Delete(ctx, IntfOnuUniID); err != nil {
+ log.Errorw("Failed to delete techprofile id resource in KV store", log.Fields{"path": IntfOnuUniID})
+ return err
+ }
+ return nil
+}
+
+// RemoveTechProfileIDForOnu deletes a specific tech profile id from the KV-Store for the given onu based on the path
+// This path is formed as the following: {IntfID, OnuID, UniID}/tp_id
+func (RsrcMgr *OpenOltResourceMgr) RemoveTechProfileIDForOnu(ctx context.Context, IntfID uint32, OnuID uint32, UniID uint32, TpID uint32) error {
+ tpIDList := RsrcMgr.GetTechProfileIDForOnu(ctx, IntfID, OnuID, UniID)
+ for i, tpIDInList := range tpIDList {
+ if tpIDInList == TpID {
+ tpIDList = append(tpIDList[:i], tpIDList[i+1:]...)
+ }
+ }
+ IntfOnuUniID := fmt.Sprintf(TpIDPathSuffix, IntfID, OnuID, UniID)
+ Value, err := json.Marshal(tpIDList)
+ if err != nil {
+ log.Error("failed to Marshal")
+ return err
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, IntfOnuUniID, Value); err != nil {
+ log.Errorf("Failed to update resource %s", IntfOnuUniID)
+ return err
+ }
+ return err
+}
+
+// UpdateTechProfileIDForOnu updates (put) already present tech-profile-id for the given onu based on the path
+// This path is formed as the following: {IntfID, OnuID, UniID}/tp_id
+func (RsrcMgr *OpenOltResourceMgr) UpdateTechProfileIDForOnu(ctx context.Context, IntfID uint32, OnuID uint32,
+ UniID uint32, TpID uint32) error {
+ var Value []byte
+ var err error
+
+ IntfOnuUniID := fmt.Sprintf(TpIDPathSuffix, IntfID, OnuID, UniID)
+
+ tpIDList := RsrcMgr.GetTechProfileIDForOnu(ctx, IntfID, OnuID, UniID)
+ for _, value := range tpIDList {
+ if value == TpID {
+ log.Debugf("TpID %d is already in tpIdList for the path %s", TpID, IntfOnuUniID)
+ return err
+ }
+ }
+ log.Debugf("updating tp id %d on path %s", TpID, IntfOnuUniID)
+ tpIDList = append(tpIDList, TpID)
+ Value, err = json.Marshal(tpIDList)
+ if err != nil {
+ log.Error("failed to Marshal")
+ return err
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, IntfOnuUniID, Value); err != nil {
+ log.Errorf("Failed to update resource %s", IntfOnuUniID)
+ return err
+ }
+ return err
+}
+
+// UpdateMeterIDForOnu updates the meter id in the KV-Store for the given onu based on the path
+// This path is formed as the following: <(pon_id, onu_id, uni_id)>/<tp_id>/meter_id/<direction>
+func (RsrcMgr *OpenOltResourceMgr) UpdateMeterIDForOnu(ctx context.Context, Direction string, IntfID uint32, OnuID uint32,
+ UniID uint32, TpID uint32, MeterConfig *ofp.OfpMeterConfig) error {
+ var Value []byte
+ var err error
+
+ IntfOnuUniID := fmt.Sprintf(MeterIDPathSuffix, IntfID, OnuID, UniID, TpID, Direction)
+ Value, err = json.Marshal(*MeterConfig)
+ if err != nil {
+ log.Error("failed to Marshal meter config")
+ return err
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, IntfOnuUniID, Value); err != nil {
+ log.Errorf("Failed to store meter into KV store %s", IntfOnuUniID)
+ return err
+ }
+ return err
+}
+
+// GetMeterIDForOnu fetches the meter id from the kv store for the given onu based on the path
+// This path is formed as the following: <(pon_id, onu_id, uni_id)>/<tp_id>/meter_id/<direction>
+func (RsrcMgr *OpenOltResourceMgr) GetMeterIDForOnu(ctx context.Context, Direction string, IntfID uint32, OnuID uint32,
+ UniID uint32, TpID uint32) (*ofp.OfpMeterConfig, error) {
+ Path := fmt.Sprintf(MeterIDPathSuffix, IntfID, OnuID, UniID, TpID, Direction)
+ var meterConfig ofp.OfpMeterConfig
+ Value, err := RsrcMgr.KVStore.Get(ctx, Path)
+ if err == nil {
+ if Value != nil {
+ log.Debug("Found meter in KV store", log.Fields{"Direction": Direction})
+ Val, er := kvstore.ToByte(Value.Value)
+ if er != nil {
+ log.Errorw("Failed to convert into byte array", log.Fields{"error": er})
+ return nil, er
+ }
+ if er = json.Unmarshal(Val, &meterConfig); er != nil {
+ log.Error("Failed to unmarshal meterconfig", log.Fields{"error": er})
+ return nil, er
+ }
+ } else {
+ log.Debug("meter-does-not-exists-in-KVStore")
+ return nil, err
+ }
+ } else {
+ log.Errorf("Failed to get Meter config from kvstore for path %s", Path)
+
+ }
+ return &meterConfig, err
+}
+
+// RemoveMeterIDForOnu deletes the meter id from the kV-Store for the given onu based on the path
+// This path is formed as the following: <(pon_id, onu_id, uni_id)>/<tp_id>/meter_id/<direction>
+func (RsrcMgr *OpenOltResourceMgr) RemoveMeterIDForOnu(ctx context.Context, Direction string, IntfID uint32, OnuID uint32,
+ UniID uint32, TpID uint32) error {
+ Path := fmt.Sprintf(MeterIDPathSuffix, IntfID, OnuID, UniID, TpID, Direction)
+ if err := RsrcMgr.KVStore.Delete(ctx, Path); err != nil {
+ log.Errorf("Failed to delete meter id %s from kvstore ", Path)
+ return err
+ }
+ return nil
+}
+
+func getFlowIDFromFlowInfo(FlowInfo *[]FlowInfo, flowID, gemportID uint32, flowStoreCookie uint64, flowCategory string, vlanPcp ...uint32) error {
+ if FlowInfo != nil {
+ for _, Info := range *FlowInfo {
+ if int32(gemportID) == Info.Flow.GemportId && flowCategory != "" && Info.FlowCategory == flowCategory {
+ log.Debug("Found flow matching with flow category", log.Fields{"flowId": flowID, "FlowCategory": flowCategory})
+ if Info.FlowCategory == "HSIA_FLOW" && Info.Flow.Classifier.OPbits == vlanPcp[0] {
+ log.Debug("Found matching vlan pcp ", log.Fields{"flowId": flowID, "Vlanpcp": vlanPcp[0]})
+ return nil
+ }
+ }
+ if int32(gemportID) == Info.Flow.GemportId && flowStoreCookie != 0 && Info.FlowStoreCookie == flowStoreCookie {
+ if flowCategory != "" && Info.FlowCategory == flowCategory {
+ log.Debug("Found flow matching with flow category", log.Fields{"flowId": flowID, "FlowCategory": flowCategory})
+ return nil
+ }
+ }
+ }
+ }
+ log.Debugw("the flow can be related to a different service", log.Fields{"flow_info": FlowInfo})
+ return errors.New("invalid flow-info")
+}
+
+//AddGemToOnuGemInfo adds gemport to onugem info kvstore
+func (RsrcMgr *OpenOltResourceMgr) AddGemToOnuGemInfo(ctx context.Context, intfID uint32, onuID uint32, gemPort uint32) error {
+ var onuGemData []OnuGemInfo
+ var err error
+
+ if err = RsrcMgr.ResourceMgrs[intfID].GetOnuGemInfo(ctx, intfID, &onuGemData); err != nil {
+ log.Errorf("failed to get onuifo for intfid %d", intfID)
+ return err
+ }
+ if len(onuGemData) == 0 {
+ log.Errorw("failed to ger Onuid info ", log.Fields{"intfid": intfID, "onuid": onuID})
+ return err
+ }
+
+ for idx, onugem := range onuGemData {
+ if onugem.OnuID == onuID {
+ for _, gem := range onuGemData[idx].GemPorts {
+ if gem == gemPort {
+ log.Debugw("Gem already present in onugem info, skpping addition", log.Fields{"gem": gem})
+ return nil
+ }
+ }
+ log.Debugw("Added gem to onugem info", log.Fields{"gem": gemPort})
+ onuGemData[idx].GemPorts = append(onuGemData[idx].GemPorts, gemPort)
+ break
+ }
+ }
+ err = RsrcMgr.ResourceMgrs[intfID].AddOnuGemInfo(ctx, intfID, onuGemData)
+ if err != nil {
+ log.Error("Failed to add onugem to kv store")
+ return err
+ }
+ return err
+}
+
+//GetOnuGemInfo gets onu gem info from the kvstore per interface
+func (RsrcMgr *OpenOltResourceMgr) GetOnuGemInfo(ctx context.Context, IntfID uint32) ([]OnuGemInfo, error) {
+ var onuGemData []OnuGemInfo
+
+ if err := RsrcMgr.ResourceMgrs[IntfID].GetOnuGemInfo(ctx, IntfID, &onuGemData); err != nil {
+ log.Errorf("failed to get onuifo for intfid %d", IntfID)
+ return nil, err
+ }
+
+ return onuGemData, nil
+}
+
+// AddOnuGemInfo adds onu info on to the kvstore per interface
+func (RsrcMgr *OpenOltResourceMgr) AddOnuGemInfo(ctx context.Context, IntfID uint32, onuGem OnuGemInfo) error {
+ var onuGemData []OnuGemInfo
+ var err error
+
+ if err = RsrcMgr.ResourceMgrs[IntfID].GetOnuGemInfo(ctx, IntfID, &onuGemData); err != nil {
+ log.Errorf("failed to get onuifo for intfid %d", IntfID)
+ return err
+ }
+ onuGemData = append(onuGemData, onuGem)
+ err = RsrcMgr.ResourceMgrs[IntfID].AddOnuGemInfo(ctx, IntfID, onuGemData)
+ if err != nil {
+ log.Error("Failed to add onugem to kv store")
+ return err
+ }
+
+ log.Debugw("added onu to onugeminfo", log.Fields{"intf": IntfID, "onugem": onuGem})
+ return err
+}
+
+// UpdateOnuGemInfo updates Onuinfo on the kvstore per interface
+func (RsrcMgr *OpenOltResourceMgr) UpdateOnuGemInfo(ctx context.Context, IntfID uint32, onuGem []OnuGemInfo) error {
+
+ // TODO: VOL-2643
+ err := RsrcMgr.ResourceMgrs[IntfID].AddOnuGemInfo(ctx, IntfID, onuGem)
+ if err != nil {
+ log.Debugw("persistence-update-failed", log.Fields{
+ "interface-id": IntfID,
+ "gem-info": onuGem,
+ "error": err})
+ return err
+ }
+
+ log.Debugw("updated onugeminfo", log.Fields{"intf": IntfID, "onugem": onuGem})
+ return nil
+}
+
+// AddUniPortToOnuInfo adds uni port to the onuinfo kvstore. check if the uni is already present if not update the kv store.
+func (RsrcMgr *OpenOltResourceMgr) AddUniPortToOnuInfo(ctx context.Context, intfID uint32, onuID uint32, portNo uint32) {
+ var onuGemData []OnuGemInfo
+ var err error
+
+ if err = RsrcMgr.ResourceMgrs[intfID].GetOnuGemInfo(ctx, intfID, &onuGemData); err != nil {
+ log.Errorf("failed to get onuifo for intfid %d", intfID)
+ return
+ }
+ for idx, onu := range onuGemData {
+ if onu.OnuID == onuID {
+ for _, uni := range onu.UniPorts {
+ if uni == portNo {
+ log.Debugw("uni already present in onugem info", log.Fields{"uni": portNo})
+ return
+ }
+ }
+ onuGemData[idx].UniPorts = append(onuGemData[idx].UniPorts, portNo)
+ break
+ }
+ }
+ err = RsrcMgr.ResourceMgrs[intfID].AddOnuGemInfo(ctx, intfID, onuGemData)
+ if err != nil {
+ log.Errorw("Failed to add uin port in onugem to kv store", log.Fields{"uni": portNo})
+ return
+ }
+ return
+}
+
+//UpdateGemPortForPktIn updates gemport for pkt in path to kvstore, path being intfid, onuid, portno
+func (RsrcMgr *OpenOltResourceMgr) UpdateGemPortForPktIn(ctx context.Context, pktIn PacketInInfoKey, gemPort uint32) {
+
+ path := fmt.Sprintf(OnuPacketINPath, pktIn.IntfID, pktIn.OnuID, pktIn.LogicalPort)
+ Value, err := json.Marshal(gemPort)
+ if err != nil {
+ log.Error("Failed to marshal data")
+ return
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, path, Value); err != nil {
+ log.Errorw("Failed to put to kvstore", log.Fields{"path": path, "value": gemPort})
+ return
+ }
+ log.Debugw("added gem packet in successfully", log.Fields{"path": path, "gem": gemPort})
+
+ return
+}
+
+// GetGemPortFromOnuPktIn gets the gem port from onu pkt in path, path being intfid, onuid, portno
+func (RsrcMgr *OpenOltResourceMgr) GetGemPortFromOnuPktIn(ctx context.Context, intfID uint32, onuID uint32, logicalPort uint32) (uint32, error) {
+
+ var Val []byte
+ var gemPort uint32
+
+ path := fmt.Sprintf(OnuPacketINPath, intfID, onuID, logicalPort)
+
+ value, err := RsrcMgr.KVStore.Get(ctx, path)
+ if err != nil {
+ log.Errorw("Failed to get from kv store", log.Fields{"path": path})
+ return uint32(0), err
+ } else if value == nil {
+ log.Debugw("No pkt in gem found", log.Fields{"path": path})
+ return uint32(0), nil
+ }
+
+ if Val, err = kvstore.ToByte(value.Value); err != nil {
+ log.Error("Failed to convert to byte array")
+ return uint32(0), err
+ }
+ if err = json.Unmarshal(Val, &gemPort); err != nil {
+ log.Error("Failed to unmarshall")
+ return uint32(0), err
+ }
+ log.Debugw("found packein gemport from path", log.Fields{"path": path, "gem": gemPort})
+
+ return gemPort, nil
+}
+
+// DelGemPortPktIn deletes the gemport from the pkt in path
+func (RsrcMgr *OpenOltResourceMgr) DelGemPortPktIn(ctx context.Context, intfID uint32, onuID uint32, logicalPort uint32) error {
+
+ path := fmt.Sprintf(OnuPacketINPath, intfID, onuID, logicalPort)
+ if err := RsrcMgr.KVStore.Delete(ctx, path); err != nil {
+ log.Errorf("Falied to remove resource %s", path)
+ return err
+ }
+ return nil
+}
+
+// DelOnuGemInfoForIntf deletes the onugem info from kvstore per interface
+func (RsrcMgr *OpenOltResourceMgr) DelOnuGemInfoForIntf(ctx context.Context, intfID uint32) error {
+ if err := RsrcMgr.ResourceMgrs[intfID].DelOnuGemInfoForIntf(ctx, intfID); err != nil {
+ log.Errorw("failed to delete onu gem info for", log.Fields{"intfid": intfID})
+ return err
+ }
+ return nil
+}
+
+//GetNNIFromKVStore gets NNi intfids from kvstore. path being per device
+func (RsrcMgr *OpenOltResourceMgr) GetNNIFromKVStore(ctx context.Context) ([]uint32, error) {
+
+ var nni []uint32
+ var Val []byte
+
+ path := fmt.Sprintf(NnniIntfID)
+ value, err := RsrcMgr.KVStore.Get(ctx, path)
+ if err != nil {
+ log.Error("failed to get data from kv store")
+ return nil, err
+ }
+ if value != nil {
+ if Val, err = kvstore.ToByte(value.Value); err != nil {
+ log.Error("Failed to convert to byte array")
+ return nil, err
+ }
+ if err = json.Unmarshal(Val, &nni); err != nil {
+ log.Error("Failed to unmarshall")
+ return nil, err
+ }
+ }
+ return nni, err
+}
+
+// AddNNIToKVStore adds Nni interfaces to kvstore, path being per device.
+func (RsrcMgr *OpenOltResourceMgr) AddNNIToKVStore(ctx context.Context, nniIntf uint32) error {
+ var Value []byte
+
+ nni, err := RsrcMgr.GetNNIFromKVStore(ctx)
+ if err != nil {
+ log.Error("failed to fetch nni interfaces from kv store")
+ return err
+ }
+
+ path := fmt.Sprintf(NnniIntfID)
+ nni = append(nni, nniIntf)
+ Value, err = json.Marshal(nni)
+ if err != nil {
+ log.Error("Failed to marshal data")
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, path, Value); err != nil {
+ log.Errorw("Failed to put to kvstore", log.Fields{"path": path, "value": Value})
+ return err
+ }
+ log.Debugw("added nni to kv successfully", log.Fields{"path": path, "nni": nniIntf})
+ return nil
+}
+
+// DelNNiFromKVStore deletes nni interface list from kv store.
+func (RsrcMgr *OpenOltResourceMgr) DelNNiFromKVStore(ctx context.Context) error {
+
+ path := fmt.Sprintf(NnniIntfID)
+
+ if err := RsrcMgr.KVStore.Delete(ctx, path); err != nil {
+ log.Errorw("Failed to delete nni interfaces from kv store", log.Fields{"path": path})
+ return err
+ }
+ return nil
+}
+
+//UpdateFlowIDsForGem updates flow id per gemport
+func (RsrcMgr *OpenOltResourceMgr) UpdateFlowIDsForGem(ctx context.Context, intf uint32, gem uint32, flowIDs []uint32) error {
+ var val []byte
+ path := fmt.Sprintf(FlowIDsForGem, intf)
+
+ flowsForGem, err := RsrcMgr.GetFlowIDsGemMapForInterface(ctx, intf)
+ if err != nil {
+ log.Error("Failed to ger flowids for interface", log.Fields{"error": err, "intf": intf})
+ return err
+ }
+ if flowsForGem == nil {
+ flowsForGem = make(map[uint32][]uint32)
+ }
+ flowsForGem[gem] = flowIDs
+ val, err = json.Marshal(flowsForGem)
+ if err != nil {
+ log.Error("Failed to marshal data", log.Fields{"error": err})
+ return err
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, path, val); err != nil {
+ log.Errorw("Failed to put to kvstore", log.Fields{"error": err, "path": path, "value": val})
+ return err
+ }
+ log.Debugw("added flowid list for gem to kv successfully", log.Fields{"path": path, "flowidlist": flowsForGem[gem]})
+ return nil
+}
+
+//DeleteFlowIDsForGem deletes the flowID list entry per gem from kvstore.
+func (RsrcMgr *OpenOltResourceMgr) DeleteFlowIDsForGem(ctx context.Context, intf uint32, gem uint32) {
+ path := fmt.Sprintf(FlowIDsForGem, intf)
+ var val []byte
+
+ flowsForGem, err := RsrcMgr.GetFlowIDsGemMapForInterface(ctx, intf)
+ if err != nil {
+ log.Error("Failed to ger flowids for interface", log.Fields{"error": err, "intf": intf})
+ return
+ }
+ if flowsForGem == nil {
+ log.Error("No flowids found ", log.Fields{"intf": intf, "gemport": gem})
+ return
+ }
+ // once we get the flows per gem map from kv , just delete the gem entry from the map
+ delete(flowsForGem, gem)
+ // once gem entry is deleted update the kv store.
+ val, err = json.Marshal(flowsForGem)
+ if err != nil {
+ log.Error("Failed to marshal data", log.Fields{"error": err})
+ return
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, path, val); err != nil {
+ log.Errorw("Failed to put to kvstore", log.Fields{"error": err, "path": path, "value": val})
+ return
+ }
+ return
+}
+
+//GetFlowIDsGemMapForInterface gets flowids per gemport and interface
+func (RsrcMgr *OpenOltResourceMgr) GetFlowIDsGemMapForInterface(ctx context.Context, intf uint32) (map[uint32][]uint32, error) {
+ path := fmt.Sprintf(FlowIDsForGem, intf)
+ var flowsForGem map[uint32][]uint32
+ var val []byte
+
+ value, err := RsrcMgr.KVStore.Get(ctx, path)
+ if err != nil {
+ log.Error("failed to get data from kv store")
+ return nil, err
+ }
+ if value != nil && value.Value != nil {
+ if val, err = kvstore.ToByte(value.Value); err != nil {
+ log.Error("Failed to convert to byte array ", log.Fields{"error": err})
+ return nil, err
+ }
+ if err = json.Unmarshal(val, &flowsForGem); err != nil {
+ log.Error("Failed to unmarshall", log.Fields{"error": err})
+ return nil, err
+ }
+ }
+ return flowsForGem, nil
+}
+
+//DeleteIntfIDGempMapPath deletes the intf id path used to store flow ids per gem to kvstore.
+func (RsrcMgr *OpenOltResourceMgr) DeleteIntfIDGempMapPath(ctx context.Context, intf uint32) {
+ path := fmt.Sprintf(FlowIDsForGem, intf)
+ if err := RsrcMgr.KVStore.Delete(ctx, path); err != nil {
+ log.Errorw("Failed to delete nni interfaces from kv store", log.Fields{"path": path})
+ return
+ }
+ return
+}
+
+// RemoveResourceMap Clear resource map associated with (intfid, onuid, uniid) tuple.
+func (RsrcMgr *OpenOltResourceMgr) RemoveResourceMap(ctx context.Context, intfID uint32, onuID int32, uniID int32) {
+ IntfOnuIDUniID := fmt.Sprintf("%d,%d,%d", intfID, onuID, uniID)
+ RsrcMgr.ResourceMgrs[intfID].RemoveResourceMap(ctx, IntfOnuIDUniID)
+}
+
+//GetMcastQueuePerInterfaceMap gets multicast queue info per pon interface
+func (RsrcMgr *OpenOltResourceMgr) GetMcastQueuePerInterfaceMap(ctx context.Context) (map[uint32][]uint32, error) {
+ path := fmt.Sprintf(McastQueuesForIntf)
+ var mcastQueueToIntfMap map[uint32][]uint32
+ var val []byte
+
+ kvPair, err := RsrcMgr.KVStore.Get(ctx, path)
+ if err != nil {
+ log.Error("failed to get data from kv store")
+ return nil, err
+ }
+ if kvPair != nil && kvPair.Value != nil {
+ if val, err = kvstore.ToByte(kvPair.Value); err != nil {
+ log.Error("Failed to convert to byte array ", log.Fields{"error": err})
+ return nil, err
+ }
+ if err = json.Unmarshal(val, &mcastQueueToIntfMap); err != nil {
+ log.Error("Failed to unmarshall ", log.Fields{"error": err})
+ return nil, err
+ }
+ }
+ return mcastQueueToIntfMap, nil
+}
+
+//AddMcastQueueForIntf adds multicast queue for pon interface
+func (RsrcMgr *OpenOltResourceMgr) AddMcastQueueForIntf(ctx context.Context, intf uint32, gem uint32, servicePriority uint32) error {
+ var val []byte
+ path := fmt.Sprintf(McastQueuesForIntf)
+
+ mcastQueues, err := RsrcMgr.GetMcastQueuePerInterfaceMap(ctx)
+ if err != nil {
+ log.Errorw("Failed to get multicast queue info for interface", log.Fields{"error": err, "intf": intf})
+ return err
+ }
+ if mcastQueues == nil {
+ mcastQueues = make(map[uint32][]uint32)
+ }
+ mcastQueues[intf] = []uint32{gem, servicePriority}
+ if val, err = json.Marshal(mcastQueues); err != nil {
+ log.Errorw("Failed to marshal data", log.Fields{"error": err})
+ return err
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, path, val); err != nil {
+ log.Errorw("Failed to put to kvstore", log.Fields{"error": err, "path": path, "value": val})
+ return err
+ }
+ log.Debugw("added multicast queue info to KV store successfully", log.Fields{"path": path, "mcastQueueInfo": mcastQueues[intf], "interfaceId": intf})
+ return nil
+}
+
+//AddFlowGroupToKVStore adds flow group into KV store
+func (RsrcMgr *OpenOltResourceMgr) AddFlowGroupToKVStore(ctx context.Context, groupEntry *ofp.OfpGroupEntry, cached bool) error {
+ var Value []byte
+ var err error
+ var path string
+ if cached {
+ path = fmt.Sprintf(FlowGroupCached, groupEntry.Desc.GroupId)
+ } else {
+ path = fmt.Sprintf(FlowGroup, groupEntry.Desc.GroupId)
+ }
+ //build group info object
+ var outPorts []uint32
+ for _, ofBucket := range groupEntry.Desc.Buckets {
+ for _, ofAction := range ofBucket.Actions {
+ if ofAction.Type == ofp.OfpActionType_OFPAT_OUTPUT {
+ outPorts = append(outPorts, ofAction.GetOutput().Port)
+ }
+ }
+ }
+ groupInfo := GroupInfo{
+ GroupID: groupEntry.Desc.GroupId,
+ OutPorts: outPorts,
+ }
+
+ Value, err = json.Marshal(groupInfo)
+
+ if err != nil {
+ log.Error("failed to Marshal flow group object")
+ return err
+ }
+
+ if err = RsrcMgr.KVStore.Put(ctx, path, Value); err != nil {
+ log.Errorf("Failed to update resource %s", path)
+ return err
+ }
+ return nil
+}
+
+//RemoveFlowGroupFromKVStore removes flow group from KV store
+func (RsrcMgr *OpenOltResourceMgr) RemoveFlowGroupFromKVStore(ctx context.Context, groupID uint32, cached bool) bool {
+ var path string
+ if cached {
+ path = fmt.Sprintf(FlowGroupCached, groupID)
+ } else {
+ path = fmt.Sprintf(FlowGroup, groupID)
+ }
+ if err := RsrcMgr.KVStore.Delete(ctx, path); err != nil {
+ log.Errorf("Failed to remove resource %s due to %s", path, err)
+ return false
+ }
+ return true
+}
+
+//GetFlowGroupFromKVStore fetches flow group from the KV store. Returns (false, {} error) if any problem occurs during
+//fetching the data. Returns (true, groupInfo, nil) if the group is fetched successfully.
+// Returns (false, {}, nil) if the group does not exists in the KV store.
+func (RsrcMgr *OpenOltResourceMgr) GetFlowGroupFromKVStore(ctx context.Context, groupID uint32, cached bool) (bool, GroupInfo, error) {
+ var groupInfo GroupInfo
+ var path string
+ if cached {
+ path = fmt.Sprintf(FlowGroupCached, groupID)
+ } else {
+ path = fmt.Sprintf(FlowGroup, groupID)
+ }
+ kvPair, err := RsrcMgr.KVStore.Get(ctx, path)
+ if err != nil {
+ return false, groupInfo, err
+ }
+ if kvPair != nil && kvPair.Value != nil {
+ Val, err := kvstore.ToByte(kvPair.Value)
+ if err != nil {
+ log.Errorw("Failed to convert flow group into byte array", log.Fields{"error": err})
+ return false, groupInfo, err
+ }
+ if err = json.Unmarshal(Val, &groupInfo); err != nil {
+ log.Errorw("Failed to unmarshal", log.Fields{"error": err})
+ return false, groupInfo, err
+ }
+ return true, groupInfo, nil
+ }
+ return false, groupInfo, nil
+}
diff --git a/internal/pkg/resourcemanager/resourcemanager_test.go b/internal/pkg/resourcemanager/resourcemanager_test.go
new file mode 100644
index 0000000..5d96cc3
--- /dev/null
+++ b/internal/pkg/resourcemanager/resourcemanager_test.go
@@ -0,0 +1,1183 @@
+/*
+ * 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.
+ */
+
+/*
+This file contains unit test cases for functions in the file resourcemanager.go.
+This file also implements the Client interface to mock the kv-client, fields struct to mock OpenOltResourceMgr
+and few utility functions.
+*/
+
+//Package adaptercore provides the utility for olt devices, flows and statistics
+package resourcemanager
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "github.com/opencord/voltha-lib-go/v3/pkg/db"
+ "github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
+ fu "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ ponrmgr "github.com/opencord/voltha-lib-go/v3/pkg/ponresourcemanager"
+ ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ "github.com/opencord/voltha-protos/v3/go/openolt"
+ "reflect"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+)
+
+func init() {
+ log.SetDefaultLogger(log.JSON, log.DebugLevel, nil)
+}
+
+const (
+ // MeterConfig meter to extract meter
+ MeterConfig = "meter_id"
+ // TpIDSuffixPath to extract Techprofile
+ TpIDSuffixPath = "tp_id"
+ // FlowIDInfo to extract flows
+ FlowIDInfo = "flow_id_info"
+ // FlowIds to extract flows
+ FlowIDs = "flow_ids"
+ // GemportIDs to gemport_ids
+ GemportIDs = "gemport_ids"
+ // AllocIDs to extract alloc_ids
+ AllocIDs = "alloc_ids"
+ // GemportIDPool to extract gemport
+ GemportIDPool = "gemport_id_pool"
+ // AllocIDPool to extract allocid
+ AllocIDPool = "alloc_id_pool"
+ // FlowIDpool to extract Flow ids
+ FlowIDpool = "flow_id_pool"
+)
+
+// fields mocks OpenOltResourceMgr struct.
+type fields struct {
+ DeviceID string
+ HostAndPort string
+ Args string
+ KVStore *db.Backend
+ DeviceType string
+ Host string
+ Port int
+ DevInfo *openolt.DeviceInfo
+ ResourceMgrs map[uint32]*ponrmgr.PONResourceManager
+}
+
+// MockKVClient mocks the AdapterProxy interface.
+type MockResKVClient struct {
+}
+
+// getResMgr mocks OpenOltResourceMgr struct.
+func getResMgr() *fields {
+ var resMgr fields
+ resMgr.KVStore = &db.Backend{
+ Client: &MockResKVClient{},
+ }
+ resMgr.ResourceMgrs = make(map[uint32]*ponrmgr.PONResourceManager)
+ ranges := make(map[string]interface{})
+ sharedIdxByType := make(map[string]string)
+ sharedIdxByType["ALLOC_ID"] = "ALLOC_ID"
+ sharedIdxByType["ONU_ID"] = "ONU_ID"
+ sharedIdxByType["GEMPORT_ID"] = "GEMPORT_ID"
+ sharedIdxByType["FLOW_ID"] = "FLOW_ID"
+ ranges["ONU_ID"] = uint32(0)
+ ranges["GEMPORT_ID"] = uint32(0)
+ ranges["ALLOC_ID"] = uint32(0)
+ ranges["FLOW_ID"] = uint32(0)
+ ranges["onu_id_shared"] = uint32(0)
+ ranges["alloc_id_shared"] = uint32(0)
+ ranges["gemport_id_shared"] = uint32(0)
+ ranges["flow_id_shared"] = uint32(0)
+ ponMgr := &ponrmgr.PONResourceManager{
+ DeviceID: "onu-1",
+ IntfIDs: []uint32{1, 2},
+ KVStore: &db.Backend{
+ Client: &MockResKVClient{},
+ },
+ PonResourceRanges: ranges,
+ SharedIdxByType: sharedIdxByType,
+ }
+ resMgr.ResourceMgrs[1] = ponMgr
+ resMgr.ResourceMgrs[2] = ponMgr
+ return &resMgr
+}
+
+// List function implemented for KVClient.
+func (kvclient *MockResKVClient) List(ctx context.Context, key string) (map[string]*kvstore.KVPair, error) {
+ return nil, errors.New("key didn't find")
+}
+
+// Get mock function implementation for KVClient
+func (kvclient *MockResKVClient) Get(ctx context.Context, key string) (*kvstore.KVPair, error) {
+ log.Debugw("Warning Warning Warning: Get of MockKVClient called", log.Fields{"key": key})
+ if key != "" {
+ if strings.Contains(key, MeterConfig) {
+ var bands []*ofp.OfpMeterBandHeader
+ bands = append(bands, &ofp.OfpMeterBandHeader{Type: ofp.OfpMeterBandType_OFPMBT_DSCP_REMARK,
+ Rate: 1024, Data: &ofp.OfpMeterBandHeader_DscpRemark{DscpRemark: &ofp.OfpMeterBandDscpRemark{PrecLevel: 2}}})
+
+ bands = append(bands, &ofp.OfpMeterBandHeader{Type: ofp.OfpMeterBandType_OFPMBT_DSCP_REMARK,
+ Rate: 1024, Data: &ofp.OfpMeterBandHeader_DscpRemark{DscpRemark: &ofp.OfpMeterBandDscpRemark{PrecLevel: 3}}})
+
+ sep := strings.Split(key, "/")[1]
+ val, _ := strconv.ParseInt(strings.Split(sep, ",")[1], 10, 32)
+ if uint32(val) > 1 {
+ meterConfig := &ofp.OfpMeterConfig{MeterId: uint32(val), Bands: bands}
+ str, _ := json.Marshal(meterConfig)
+
+ return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
+ }
+ return nil, errors.New("invalid meter")
+ }
+ if strings.Contains(key, FlowIDpool) || strings.Contains(key, GemportIDPool) || strings.Contains(key, AllocIDPool) {
+ log.Debug("Error Error Error Key:", FlowIDpool, GemportIDPool, AllocIDPool)
+ data := make(map[string]interface{})
+ data["pool"] = "1024"
+ data["start_idx"] = 1
+ data["end_idx"] = 1024
+ str, _ := json.Marshal(data)
+ return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
+ }
+ if strings.Contains(key, FlowIDInfo) || strings.Contains(key, FlowIDs) {
+ log.Debug("Error Error Error Key:", FlowIDs, FlowIDInfo)
+ str, _ := json.Marshal([]uint32{1, 2})
+ return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
+ }
+ if strings.Contains(key, AllocIDs) || strings.Contains(key, GemportIDs) {
+ log.Debug("Error Error Error Key:", AllocIDs, GemportIDs)
+ str, _ := json.Marshal(1)
+ return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
+ }
+ if strings.Contains(key, McastQueuesForIntf) {
+ log.Debug("Error Error Error Key:", McastQueuesForIntf)
+ mcastQueues := make(map[uint32][]uint32)
+ mcastQueues[10] = []uint32{4000, 0}
+ str, _ := json.Marshal(mcastQueues)
+ return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
+ }
+ if strings.Contains(key, "flow_groups") && !strings.Contains(key, "1000") {
+ groupInfo := GroupInfo{GroupID: 2, OutPorts: []uint32{2}}
+ str, _ := json.Marshal(groupInfo)
+ return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
+ }
+
+ maps := make(map[string]*kvstore.KVPair)
+ maps[key] = &kvstore.KVPair{Key: key}
+ return maps[key], nil
+ }
+ return nil, errors.New("key didn't find")
+}
+
+// Put mock function implementation for KVClient
+func (kvclient *MockResKVClient) Put(ctx context.Context, key string, value interface{}) error {
+ if key != "" {
+ return nil
+ }
+ return errors.New("key didn't find")
+}
+
+// Delete mock function implementation for KVClient
+func (kvclient *MockResKVClient) Delete(ctx context.Context, key string) error {
+ return nil
+}
+
+// Reserve mock function implementation for KVClient
+func (kvclient *MockResKVClient) Reserve(ctx context.Context, key string, value interface{}, ttl int64) (interface{}, error) {
+ return nil, errors.New("key didn't find")
+}
+
+// ReleaseReservation mock function implementation for KVClient
+func (kvclient *MockResKVClient) ReleaseReservation(ctx context.Context, key string) error {
+ return nil
+}
+
+// ReleaseAllReservations mock function implementation for KVClient
+func (kvclient *MockResKVClient) ReleaseAllReservations(ctx context.Context) error {
+ return nil
+}
+
+// RenewReservation mock function implementation for KVClient
+func (kvclient *MockResKVClient) RenewReservation(ctx context.Context, key string) error {
+ return nil
+}
+
+// Watch mock function implementation for KVClient
+func (kvclient *MockResKVClient) Watch(ctx context.Context, key string, withPrefix bool) chan *kvstore.Event {
+ return nil
+}
+
+// AcquireLock mock function implementation for KVClient
+func (kvclient *MockResKVClient) AcquireLock(ctx context.Context, lockName string, timeout int) error {
+ return nil
+}
+
+// ReleaseLock mock function implementation for KVClient
+func (kvclient *MockResKVClient) ReleaseLock(lockName string) error {
+ return nil
+}
+
+// IsConnectionUp mock function implementation for KVClient
+func (kvclient *MockResKVClient) IsConnectionUp(ctx context.Context) bool { // timeout in second
+ return true
+}
+
+// CloseWatch mock function implementation for KVClient
+func (kvclient *MockResKVClient) CloseWatch(key string, ch chan *kvstore.Event) {
+}
+
+// Close mock function implementation for KVClient
+func (kvclient *MockResKVClient) Close() {
+}
+
+// testResMgrObject maps fields type to OpenOltResourceMgr type.
+func testResMgrObject(testResMgr *fields) *OpenOltResourceMgr {
+ return &OpenOltResourceMgr{
+ DeviceID: testResMgr.DeviceID,
+ HostAndPort: testResMgr.HostAndPort,
+ Args: testResMgr.Args,
+ KVStore: testResMgr.KVStore,
+ DeviceType: testResMgr.DeviceType,
+ Host: testResMgr.Host,
+ Port: testResMgr.Port,
+ DevInfo: testResMgr.DevInfo,
+ ResourceMgrs: testResMgr.ResourceMgrs,
+ }
+}
+
+func TestNewResourceMgr(t *testing.T) {
+ type args struct {
+ deviceID string
+ KVStoreHostPort string
+ kvStoreType string
+ deviceType string
+ devInfo *openolt.DeviceInfo
+ }
+ tests := []struct {
+ name string
+ args args
+ want *OpenOltResourceMgr
+ }{
+ {"NewResourceMgr-1", args{"olt1", "1:2", "consul",
+ "onu", &openolt.DeviceInfo{OnuIdStart: 1, OnuIdEnd: 1}}, &OpenOltResourceMgr{}},
+ {"NewResourceMgr-2", args{"olt2", "3:4", "etcd",
+ "onu", &openolt.DeviceInfo{OnuIdStart: 1, OnuIdEnd: 1}}, &OpenOltResourceMgr{}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if got := NewResourceMgr(ctx, tt.args.deviceID, tt.args.KVStoreHostPort, tt.args.kvStoreType, tt.args.deviceType, tt.args.devInfo); reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("NewResourceMgr() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_Delete(t *testing.T) {
+ tests := []struct {
+ name string
+ fields *fields
+ wantErr error
+ }{
+ {"Delete-1", getResMgr(), errors.New("failed to clear device resource pool")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := RsrcMgr.Delete(ctx); (err != nil) && reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) {
+ t.Errorf("Delete() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_FreeFlowID(t *testing.T) {
+ type args struct {
+ IntfID uint32
+ onuID int32
+ uniID int32
+ FlowID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ }{
+ {"FreeFlowID-1", getResMgr(), args{2, 2, 2, 2}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ RsrcMgr.FreeFlowID(ctx, tt.args.IntfID, tt.args.onuID, tt.args.uniID, tt.args.FlowID)
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_FreeFlowIDs(t *testing.T) {
+
+ type args struct {
+ IntfID uint32
+ onuID uint32
+ uniID uint32
+ FlowID []uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ }{
+ {"FreeFlowIDs-1", getResMgr(), args{2, 2, 2, []uint32{1, 2}}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ RsrcMgr.FreeFlowIDs(ctx, tt.args.IntfID, tt.args.onuID, tt.args.uniID, tt.args.FlowID)
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_FreePONResourcesForONU(t *testing.T) {
+ type args struct {
+ intfID uint32
+ onuID uint32
+ uniID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ }{
+ {"FreePONResourcesForONU-1", getResMgr(), args{2, 0, 2}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ RsrcMgr.FreePONResourcesForONU(ctx, tt.args.intfID, tt.args.onuID, tt.args.uniID)
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_FreeonuID(t *testing.T) {
+ type args struct {
+ intfID uint32
+ onuID []uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ }{
+ {"FreeOnuID-1", getResMgr(), args{2, []uint32{1, 2}}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ RsrcMgr.FreeonuID(ctx, tt.args.intfID, tt.args.onuID)
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_GetAllocID(t *testing.T) {
+
+ type args struct {
+ intfID uint32
+ onuID uint32
+ uniID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want uint32
+ }{
+ {"GetAllocID-1", getResMgr(), args{2, 2, 2}, 0},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if got := RsrcMgr.GetAllocID(ctx, tt.args.intfID, tt.args.onuID, tt.args.uniID); reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("GetAllocID() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_GetCurrentAllocIDForOnu(t *testing.T) {
+ type args struct {
+ intfID uint32
+ onuID uint32
+ uniID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want []uint32
+ }{
+ {"GetCurrentAllocIDForOnu-1", getResMgr(), args{2, 2, 2}, []uint32{}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if got := RsrcMgr.GetCurrentAllocIDsForOnu(ctx, tt.args.intfID, tt.args.onuID, tt.args.uniID); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("GetCurrentAllocIDsForOnu() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_GetCurrentFlowIDsForOnu(t *testing.T) {
+
+ type args struct {
+ PONIntfID uint32
+ ONUID int32
+ UNIID int32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want []uint32
+ }{
+ {"GetCurrentFlowIDsForOnu-1", getResMgr(), args{2, 2, 2}, []uint32{}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if got := RsrcMgr.GetCurrentFlowIDsForOnu(ctx, tt.args.PONIntfID, tt.args.ONUID, tt.args.UNIID); reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("GetCurrentFlowIDsForOnu() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_GetCurrentGEMPortIDsForOnu(t *testing.T) {
+ type args struct {
+ intfID uint32
+ onuID uint32
+ uniID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want []uint32
+ }{
+ {"GetCurrentGEMPortIDsForOnu-1", getResMgr(), args{2, 2, 2}, []uint32{}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if got := RsrcMgr.GetCurrentGEMPortIDsForOnu(ctx, tt.args.intfID, tt.args.onuID, tt.args.uniID); reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("GetCurrentGEMPortIDsForOnu() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_GetFlowID(t *testing.T) {
+
+ type args struct {
+ ponIntfID uint32
+ ONUID int32
+ uniID int32
+ gemportID uint32
+ flowStoreCookie uint64
+ flowCategory string
+ vlanPcp []uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want uint32
+ wantErr error
+ }{
+ {"GetFlowID-1", getResMgr(), args{2, 2, 2, 2, 2,
+ "HSIA", nil}, 0, errors.New("failed to get flows")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ got, err := RsrcMgr.GetFlowID(ctx, tt.args.ponIntfID, tt.args.ONUID, tt.args.uniID, tt.args.gemportID, tt.args.flowStoreCookie, tt.args.flowCategory, tt.args.vlanPcp...)
+ if err != nil && reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) {
+ t.Errorf("GetFlowID() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("GetFlowID() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_GetGEMPortID(t *testing.T) {
+ type args struct {
+ ponPort uint32
+ onuID uint32
+ uniID uint32
+ NumOfPorts uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want []uint32
+ wantErr error
+ }{
+ {"GetGEMPortID-1", getResMgr(), args{2, 2, 2, 2}, []uint32{},
+ errors.New("failed to get gem port")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ got, err := RsrcMgr.GetGEMPortID(ctx, tt.args.ponPort, tt.args.onuID, tt.args.uniID, tt.args.NumOfPorts)
+ if reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) && err != nil {
+ t.Errorf("GetGEMPortID() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("GetGEMPortID() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_GetMeterIDForOnu(t *testing.T) {
+ type args struct {
+ Direction string
+ IntfID uint32
+ OnuID uint32
+ UniID uint32
+ tpID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want *ofp.OfpMeterConfig
+ wantErr error
+ }{
+ {"GetMeterIDOnu", getResMgr(), args{"DOWNSTREAM", 1, 1, 1, 64},
+ &ofp.OfpMeterConfig{}, errors.New("failed to get Meter config from kvstore for path")},
+ {"GetMeterIDOnu", getResMgr(), args{"DOWNSTREAM", 2, 2, 2, 65},
+ &ofp.OfpMeterConfig{}, errors.New("failed to get Meter config from kvstore for path")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ got, err := RsrcMgr.GetMeterIDForOnu(ctx, tt.args.Direction, tt.args.IntfID, tt.args.OnuID, tt.args.UniID, tt.args.tpID)
+ if reflect.TypeOf(got) != reflect.TypeOf(tt.want) && err != nil {
+ t.Errorf("GetMeterIDForOnu() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_GetONUID(t *testing.T) {
+ type args struct {
+ ponIntfID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want uint32
+ wantErr error
+ }{
+ {"GetONUID-1", getResMgr(), args{2}, uint32(0), errors.New("json errors")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ got, err := RsrcMgr.GetONUID(ctx, tt.args.ponIntfID)
+ if got != tt.want && err != nil {
+ t.Errorf("GetONUID() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_GetTechProfileIDForOnu(t *testing.T) {
+
+ type args struct {
+ IntfID uint32
+ OnuID uint32
+ UniID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want []uint32
+ }{
+ {"GetTechProfileIDForOnu-1", getResMgr(), args{2, 2, 2},
+ []uint32{1}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if got := RsrcMgr.GetTechProfileIDForOnu(ctx, tt.args.IntfID, tt.args.OnuID, tt.args.UniID); reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("GetTechProfileIDForOnu() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_IsFlowCookieOnKVStore(t *testing.T) {
+ type args struct {
+ ponIntfID uint32
+ onuID int32
+ uniID int32
+ flowStoreCookie uint64
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want bool
+ }{
+ {"IsFlowCookieOnKVStore-1", getResMgr(), args{2, 2, 2, 2}, false},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if got := RsrcMgr.IsFlowCookieOnKVStore(ctx, tt.args.ponIntfID, tt.args.onuID, tt.args.uniID, tt.args.flowStoreCookie); got != tt.want {
+ t.Errorf("IsFlowCookieOnKVStore() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_RemoveMeterIDForOnu(t *testing.T) {
+
+ type args struct {
+ Direction string
+ IntfID uint32
+ OnuID uint32
+ UniID uint32
+ tpID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"RemoveMeterIdForOnu-1", getResMgr(), args{"DOWNSTREAM", 1, 1, 1, 64},
+ errors.New("failed to delete meter id %s from kvstore")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := RsrcMgr.RemoveMeterIDForOnu(ctx, tt.args.Direction, tt.args.IntfID, tt.args.OnuID, tt.args.UniID,
+ tt.args.tpID); reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) && err != nil {
+ t.Errorf("RemoveMeterIDForOnu() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_RemoveTechProfileIDForOnu(t *testing.T) {
+ type args struct {
+ IntfID uint32
+ OnuID uint32
+ UniID uint32
+ tpID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"RemoveTechProfileIDForOnu-1", getResMgr(), args{2, 2, 2, 64},
+ errors.New("failed to delete techprofile id resource %s in KV store")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := RsrcMgr.RemoveTechProfileIDForOnu(ctx, tt.args.IntfID, tt.args.OnuID, tt.args.UniID,
+ tt.args.tpID); reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) && err != nil {
+ t.Errorf("RemoveTechProfileIDForOnu() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_UpdateAllocIdsForOnu(t *testing.T) {
+ type args struct {
+ ponPort uint32
+ onuID uint32
+ uniID uint32
+ allocID []uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"UpdateAllocIdsForOnu-1", getResMgr(), args{2, 2, 2, []uint32{1, 2}},
+ errors.New("")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := RsrcMgr.UpdateAllocIdsForOnu(ctx, tt.args.ponPort, tt.args.onuID, tt.args.uniID, tt.args.allocID); err != nil && reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) {
+ t.Errorf("UpdateAllocIdsForOnu() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_UpdateFlowIDInfo(t *testing.T) {
+ type args struct {
+ ponIntfID int32
+ onuID int32
+ uniID int32
+ flowID uint32
+ flowData *[]FlowInfo
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"UpdateFlowIDInfo-1", getResMgr(), args{2, 2, 2, 2, &[]FlowInfo{}}, errors.New("")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := RsrcMgr.UpdateFlowIDInfo(ctx, tt.args.ponIntfID, tt.args.onuID, tt.args.uniID, tt.args.flowID, tt.args.flowData); err != nil && reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) {
+ t.Errorf("UpdateFlowIDInfo() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_UpdateGEMPortIDsForOnu(t *testing.T) {
+
+ type args struct {
+ ponPort uint32
+ onuID uint32
+ uniID uint32
+ GEMPortList []uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"UpdateGEMPortIDsForOnu-1", getResMgr(), args{2, 2, 2,
+ []uint32{1, 2}}, errors.New("failed to update resource")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := RsrcMgr.UpdateGEMPortIDsForOnu(ctx, tt.args.ponPort, tt.args.onuID, tt.args.uniID, tt.args.GEMPortList); err != nil && reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) {
+ t.Errorf("UpdateGEMPortIDsForOnu() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_UpdateGEMportsPonportToOnuMapOnKVStore(t *testing.T) {
+ type args struct {
+ gemPorts []uint32
+ PonPort uint32
+ onuID uint32
+ uniID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"UpdateGEMportsPonportToOnuMapOnKVStore-1", getResMgr(), args{[]uint32{1, 2},
+ 2, 2, 2}, errors.New("failed to update resource")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := RsrcMgr.UpdateGEMportsPonportToOnuMapOnKVStore(ctx, tt.args.gemPorts, tt.args.PonPort,
+ tt.args.onuID, tt.args.uniID); err != nil && reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) {
+ t.Errorf("UpdateGEMportsPonportToOnuMapOnKVStore() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_UpdateMeterIDForOnu(t *testing.T) {
+ type args struct {
+ Direction string
+ IntfID uint32
+ OnuID uint32
+ UniID uint32
+ tpID uint32
+ MeterConfig *ofp.OfpMeterConfig
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"UpdateMeterIDForOnu-1", getResMgr(), args{"DOWNSTREAM", 2, 2,
+ 2, 64, &ofp.OfpMeterConfig{}}, errors.New("failed to get Meter config from kvstore for path")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := RsrcMgr.UpdateMeterIDForOnu(ctx, tt.args.Direction, tt.args.IntfID, tt.args.OnuID, tt.args.UniID,
+ tt.args.tpID, tt.args.MeterConfig); reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) && err != nil {
+ t.Errorf("UpdateMeterIDForOnu() got = %v, want %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_UpdateTechProfileIDForOnu(t *testing.T) {
+ type args struct {
+ IntfID uint32
+ OnuID uint32
+ UniID uint32
+ TpID uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ wantErr error
+ }{
+ {"UpdateTechProfileIDForOnu-1", getResMgr(), args{2, 2, 2,
+ 2}, errors.New("failed to update resource")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := RsrcMgr.UpdateTechProfileIDForOnu(ctx, tt.args.IntfID, tt.args.OnuID, tt.args.UniID, tt.args.TpID); reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) && err != nil {
+ t.Errorf("UpdateTechProfileIDForOnu() got = %v, want %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestSetKVClient(t *testing.T) {
+ type args struct {
+ backend string
+ Host string
+ Port int
+ DeviceID string
+ }
+ tests := []struct {
+ name string
+ args args
+ want *db.Backend
+ }{
+ {"setKVClient-1", args{"consul", "1.1.1.1", 1, "olt1"}, &db.Backend{}},
+ {"setKVClient-1", args{"etcd", "2.2.2.2", 2, "olt2"}, &db.Backend{}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := SetKVClient(tt.args.backend, tt.args.Host, tt.args.Port, tt.args.DeviceID); reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("SetKVClient() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_getFlowIDFromFlowInfo(t *testing.T) {
+ type args struct {
+ FlowInfo *[]FlowInfo
+ flowID uint32
+ gemportID uint32
+ flowStoreCookie uint64
+ flowCategory string
+ vlanPcp []uint32
+ }
+ flowInfo := &[]FlowInfo{
+ {
+ &openolt.Flow{
+ FlowId: 1,
+ GemportId: 1,
+ Classifier: &openolt.Classifier{
+ OPbits: 1,
+ }},
+ 1,
+ "HSIA_FLOW",
+ 2000,
+ },
+ {
+ &openolt.Flow{
+ GemportId: 1,
+ },
+ 1,
+ "EAPOL",
+ 3000,
+ },
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr error
+ }{
+ {"getFlowIdFromFlowInfo-1", args{}, errors.New("invalid flow-info")},
+ {"getFlowIdFromFlowInfo-2", args{flowInfo, 1, 1, 1,
+ "HSIA_FLOW", []uint32{1, 2}}, errors.New("invalid flow-info")},
+ {"getFlowIdFromFlowInfo-2", args{flowInfo, 1, 1, 1,
+ "EAPOL", []uint32{1, 2}}, errors.New("invalid flow-info")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ err := getFlowIDFromFlowInfo(tt.args.FlowInfo, tt.args.flowID, tt.args.gemportID, tt.args.flowStoreCookie, tt.args.flowCategory, tt.args.vlanPcp...)
+ if reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) && err != nil {
+ t.Errorf("getFlowIDFromFlowInfo() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if err == nil {
+ t.Log("return'd nil")
+ }
+ })
+ }
+}
+
+func Test_newKVClient(t *testing.T) {
+ type args struct {
+ storeType string
+ address string
+ timeout uint32
+ }
+ var kvClient kvstore.Client
+ tests := []struct {
+ name string
+ args args
+ want kvstore.Client
+ wantErr error
+ }{
+ {"newKVClient-1", args{"", "3.3.3.3", 1}, kvClient, errors.New("unsupported-kv-store")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := newKVClient(tt.args.storeType, tt.args.address, tt.args.timeout)
+ if got != nil && reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
+ t.Errorf("newKVClient() got = %v, want %v", got, tt.want)
+ }
+ if (err != nil) && reflect.TypeOf(err) != reflect.TypeOf(tt.wantErr) {
+ t.Errorf("newKVClient() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_AddMcastQueueForIntf(t *testing.T) {
+ type args struct {
+ intf uint32
+ gem uint32
+ servicePriority uint32
+ }
+ tests := []struct {
+ name string
+ args args
+ fields *fields
+ }{
+ {"AddMcastQueueForIntf-1", args{0, 4000, 0}, getResMgr()},
+ {"AddMcastQueueForIntf-2", args{1, 4000, 1}, getResMgr()},
+ {"AddMcastQueueForIntf-3", args{2, 4000, 2}, getResMgr()},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ err := RsrcMgr.AddMcastQueueForIntf(ctx, tt.args.intf, tt.args.gem, tt.args.servicePriority)
+ if err != nil {
+ t.Errorf("%s got err= %s wants nil", tt.name, err)
+ return
+ }
+ })
+ }
+}
+
+func newGroup(groupID uint32, outPorts []uint32) *ofp.OfpGroupEntry {
+ groupDesc := ofp.OfpGroupDesc{
+ Type: ofp.OfpGroupType_OFPGT_ALL,
+ GroupId: groupID,
+ }
+ groupEntry := ofp.OfpGroupEntry{
+ Desc: &groupDesc,
+ }
+ for i := 0; i < len(outPorts); i++ {
+ var acts []*ofp.OfpAction
+ acts = append(acts, fu.Output(outPorts[i]))
+ bucket := ofp.OfpBucket{
+ Actions: acts,
+ }
+ groupDesc.Buckets = append(groupDesc.Buckets, &bucket)
+ }
+ return &groupEntry
+}
+
+func TestOpenOltResourceMgr_AddFlowGroupToKVStore(t *testing.T) {
+ type args struct {
+ group *ofp.OfpGroupEntry
+ cached bool
+ }
+ //create group 1
+ group1 := newGroup(1, []uint32{1})
+ //create group 2
+ group2 := newGroup(2, []uint32{2})
+ //define test set
+ tests := []struct {
+ name string
+ args args
+ fields *fields
+ }{
+ {"AddFlowGroupToKVStore-1", args{group1, true}, getResMgr()},
+ {"AddFlowGroupToKVStore-2", args{group2, false}, getResMgr()},
+ }
+ //execute tests
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ err := RsrcMgr.AddFlowGroupToKVStore(ctx, tt.args.group, tt.args.cached)
+ if err != nil {
+ t.Errorf("%s got err= %s wants nil", tt.name, err)
+ return
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_RemoveFlowGroupFromKVStore(t *testing.T) {
+ type args struct {
+ groupID uint32
+ cached bool
+ }
+ //define test set
+ tests := []struct {
+ name string
+ args args
+ fields *fields
+ }{
+ {"RemoveFlowGroupFromKVStore-1", args{1, true}, getResMgr()},
+ {"RemoveFlowGroupFromKVStore-2", args{2, false}, getResMgr()},
+ }
+ //execute tests
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ success := RsrcMgr.RemoveFlowGroupFromKVStore(ctx, tt.args.groupID, tt.args.cached)
+ if !success {
+ t.Errorf("%s got false but wants true", tt.name)
+ return
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_GetFlowGroupFromKVStore(t *testing.T) {
+ type args struct {
+ groupID uint32
+ cached bool
+ }
+ //define test set
+ tests := []struct {
+ name string
+ args args
+ fields *fields
+ }{
+ {"GetFlowGroupFromKVStore-1", args{1, true}, getResMgr()},
+ {"GetFlowGroupFromKVStore-2", args{2, false}, getResMgr()},
+ {"GetFlowGroupFromKVStore-3", args{1000, false}, getResMgr()},
+ }
+ //execute tests
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ RsrcMgr := testResMgrObject(tt.fields)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ exists, groupInfo, err := RsrcMgr.GetFlowGroupFromKVStore(ctx, tt.args.groupID, tt.args.cached)
+ if err != nil {
+ t.Errorf("%s got error but wants nil error", tt.name)
+ return
+ } else if exists && (groupInfo.GroupID == 0) {
+ t.Errorf("%s got true and nil group info but expected not nil group info", tt.name)
+ return
+ } else if tt.args.groupID == 3 && exists {
+ t.Errorf("%s got true but wants false", tt.name)
+ return
+ }
+ })
+ }
+}