[VOL-1349] EPON OLT adapter (package B)
Change-Id: I634ef62c53813dcf4456f54948f13e06358e263c
diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go
new file mode 100644
index 0000000..68d1b29
--- /dev/null
+++ b/internal/pkg/config/config.go
@@ -0,0 +1,206 @@
+/*
+* 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 config provides the Log, kvstore, Kafka configuration
+package config
+
+import (
+ "flag"
+ "os"
+ "time"
+)
+
+// Open OLT default constants
+const (
+ EtcdStoreName = "etcd"
+ defaultInstanceid = "openOlt001"
+ defaultKafkaadapteraddress = "127.0.0.1:9092"
+ defaultKafkaclusteraddress = "127.0.0.1:9094"
+ defaultKvstoretype = EtcdStoreName
+ defaultKvstoretimeout = 5 * time.Second
+ defaultKvstoreaddress = "127.0.0.1:2379" // Port: Consul = 8500; Etcd = 2379
+ defaultLoglevel = "WARN"
+ defaultBanner = false
+ defaultDisplayVersionOnly = false
+ defaultTopic = "openolt"
+ defaultCoretopic = "rwcore"
+ defaultEventtopic = "voltha.events"
+ defaultOnunumber = 1
+ defaultProbeAddress = ":8080"
+ defaultLiveProbeInterval = 60 * time.Second
+ defaultNotLiveProbeInterval = 5 * time.Second // Probe more frequently when not alive
+ defaultHearbeatCheckInterval = 15 * time.Second
+ defaultHearbeatFailReportInterval = 0 * time.Second
+ defaultGrpcTimeoutInterval = 2 * time.Second
+ defaultCurrentReplica = 1
+ defaultTotalReplicas = 1
+ defaultTraceEnabled = false
+ defaultTraceAgentAddress = "127.0.0.1:6831"
+ defaultLogCorrelationEnabled = true
+ defaultInterfaceName = "eth0"
+ defaultSrcMac = ""
+)
+
+// AdapterFlags represents the set of configurations used by the read-write adaptercore service
+type AdapterFlags struct {
+ InstanceID string
+ KafkaAdapterAddress string
+ KafkaClusterAddress string
+ KVStoreType string
+ KVStoreTimeout time.Duration
+ KVStoreAddress string
+ Topic string
+ CoreTopic string
+ EventTopic string
+ LogLevel string
+ OnuNumber int
+ Banner bool
+ DisplayVersionOnly bool
+ ProbeAddress string
+ LiveProbeInterval time.Duration
+ NotLiveProbeInterval time.Duration
+ HeartbeatCheckInterval time.Duration
+ HeartbeatFailReportInterval time.Duration
+ GrpcTimeoutInterval time.Duration
+ CurrentReplica int
+ TotalReplicas int
+ TraceEnabled bool
+ TraceAgentAddress string
+ LogCorrelationEnabled bool
+ InterfaceName string
+ SrcMac string
+}
+
+// NewAdapterFlags returns a new RWCore config
+func NewAdapterFlags() *AdapterFlags {
+ var adapterFlags = AdapterFlags{ // Default values
+ InstanceID: defaultInstanceid,
+ KafkaAdapterAddress: defaultKafkaadapteraddress,
+ KafkaClusterAddress: defaultKafkaclusteraddress,
+ KVStoreType: defaultKvstoretype,
+ KVStoreTimeout: defaultKvstoretimeout,
+ KVStoreAddress: defaultKvstoreaddress,
+ Topic: defaultTopic,
+ CoreTopic: defaultCoretopic,
+ EventTopic: defaultEventtopic,
+ LogLevel: defaultLoglevel,
+ OnuNumber: defaultOnunumber,
+ Banner: defaultBanner,
+ DisplayVersionOnly: defaultDisplayVersionOnly,
+ ProbeAddress: defaultProbeAddress,
+ LiveProbeInterval: defaultLiveProbeInterval,
+ NotLiveProbeInterval: defaultNotLiveProbeInterval,
+ HeartbeatCheckInterval: defaultHearbeatCheckInterval,
+ HeartbeatFailReportInterval: defaultHearbeatFailReportInterval,
+ GrpcTimeoutInterval: defaultGrpcTimeoutInterval,
+ TraceEnabled: defaultTraceEnabled,
+ TraceAgentAddress: defaultTraceAgentAddress,
+ LogCorrelationEnabled: defaultLogCorrelationEnabled,
+ InterfaceName: defaultInterfaceName,
+ SrcMac: defaultSrcMac,
+ }
+ return &adapterFlags
+}
+
+// ParseCommandArguments parses the arguments when running read-write adaptercore service
+func (so *AdapterFlags) ParseCommandArguments() {
+
+ help := "Kafka - Adapter messaging address"
+ flag.StringVar(&(so.KafkaAdapterAddress), "kafka_adapter_address", defaultKafkaadapteraddress, help)
+
+ help = "Kafka - Cluster messaging address"
+ flag.StringVar(&(so.KafkaClusterAddress), "kafka_cluster_address", defaultKafkaclusteraddress, help)
+
+ help = "Open OLT topic"
+ flag.StringVar(&(so.Topic), "adapter_topic", defaultTopic, help)
+
+ help = "Core topic"
+ flag.StringVar(&(so.CoreTopic), "core_topic", defaultCoretopic, help)
+
+ help = "Event topic"
+ flag.StringVar(&(so.EventTopic), "event_topic", defaultEventtopic, help)
+
+ help = "KV store type"
+ flag.StringVar(&(so.KVStoreType), "kv_store_type", defaultKvstoretype, help)
+
+ help = "The default timeout when making a kv store request"
+ flag.DurationVar(&(so.KVStoreTimeout), "kv_store_request_timeout", defaultKvstoretimeout, help)
+
+ help = "KV store address"
+ flag.StringVar(&(so.KVStoreAddress), "kv_store_address", defaultKvstoreaddress, help)
+
+ help = "Log level"
+ flag.StringVar(&(so.LogLevel), "log_level", defaultLoglevel, help)
+
+ help = "Number of ONUs"
+ flag.IntVar(&(so.OnuNumber), "onu_number", defaultOnunumber, help)
+
+ help = "Show startup banner log lines"
+ flag.BoolVar(&(so.Banner), "banner", defaultBanner, help)
+
+ help = "Show version information and exit"
+ flag.BoolVar(&(so.DisplayVersionOnly), "version", defaultDisplayVersionOnly, help)
+
+ help = "The address on which to listen to answer liveness and readiness probe queries over HTTP."
+ flag.StringVar(&(so.ProbeAddress), "probe_address", defaultProbeAddress, help)
+
+ help = "Number of seconds for the default liveliness check"
+ flag.DurationVar(&(so.LiveProbeInterval), "live_probe_interval", defaultLiveProbeInterval, help)
+
+ help = "Number of seconds for liveliness check if probe is not running"
+ flag.DurationVar(&(so.NotLiveProbeInterval), "not_live_probe_interval", defaultNotLiveProbeInterval, help)
+
+ help = "Number of seconds for heartbeat check interval."
+ flag.DurationVar(&(so.HeartbeatCheckInterval), "hearbeat_check_interval", defaultHearbeatCheckInterval, help)
+
+ help = "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 = "Number of seconds for GRPC timeout."
+ flag.DurationVar(&(so.GrpcTimeoutInterval), "grpc_timeout_interval", defaultGrpcTimeoutInterval, help)
+
+ help = "Replica number of this particular instance (default: %s)"
+ flag.IntVar(&(so.CurrentReplica), "current_replica", defaultCurrentReplica, help)
+
+ help = "Total number of instances for this adapter"
+ flag.IntVar(&(so.TotalReplicas), "total_replica", defaultTotalReplicas, help)
+
+ help = "Whether to send logs to tracing agent?"
+ flag.BoolVar(&(so.TraceEnabled), "trace_enabled", defaultTraceEnabled, help)
+
+ help = "The address of tracing agent to which span info should be sent."
+ flag.StringVar(&(so.TraceAgentAddress), "trace_agent_address", defaultTraceAgentAddress, help)
+
+ help = "Whether to enrich log statements with fields denoting operation being executed for achieving correlation?"
+ flag.BoolVar(&(so.LogCorrelationEnabled), "log_correlation_enabled", defaultLogCorrelationEnabled, help)
+
+ help = "Interface name."
+ flag.StringVar(&(so.InterfaceName), "interface_name", defaultInterfaceName, help)
+
+ help = "Source mac address."
+ flag.StringVar(&(so.SrcMac), "src_mac", defaultSrcMac, 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/common.go b/internal/pkg/core/common.go
new file mode 100644
index 0000000..88b9cab
--- /dev/null
+++ b/internal/pkg/core/common.go
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//Package core Common Logger initialization
+package core
+
+import (
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+var logger log.CLogger
+
+func init() {
+ var err error
+ logger, err = log.RegisterPackage(log.JSON, log.ErrorLevel, log.Fields{})
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/internal/pkg/core/device_handler.go b/internal/pkg/core/device_handler.go
new file mode 100644
index 0000000..6b29558
--- /dev/null
+++ b/internal/pkg/core/device_handler.go
@@ -0,0 +1,2608 @@
+/*
+ * 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 (
+ "context"
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/cenkalti/backoff/v3"
+ "github.com/gogo/protobuf/proto"
+ "github.com/golang/protobuf/ptypes"
+ grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
+ grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
+ "github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+ "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "github.com/opencord/voltha-lib-go/v3/pkg/pmmetrics"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/core/l2oam"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/olterrors"
+ rsrcMgr "github.com/opencord/voltha-openolt-adapter/internal/pkg/resourcemanager"
+ "github.com/opencord/voltha-openolt-adapter/pkg/mocks"
+ "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"
+ "github.com/opentracing/opentracing-go"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+// Constants for number of retries and for timeout
+const (
+ MaxRetry = 10
+ MaxTimeOutInMs = 500
+ InvalidPort = 0xffffffff
+)
+
+// pendingFlowRemoveDataKey is key to pendingFlowRemoveDataPerSubscriber map
+type pendingFlowRemoveDataKey struct {
+ intfID uint32
+ onuID uint32
+ uniID uint32
+}
+
+// pendingFlowRemoveData is value stored in pendingFlowRemoveDataPerSubscriber map
+// This holds the number of pending flow removes and also a signal channel to
+// to indicate the receiver when all flow removes are handled
+type pendingFlowRemoveData struct {
+ pendingFlowRemoveCount uint32
+ allFlowsRemoved chan struct{}
+}
+
+//DeviceHandler will interact with the OLT device.
+type DeviceHandler struct {
+ 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
+ stopIndications chan bool
+ isReadIndicationRoutineActive bool
+
+ pendingFlowRemoveDataPerSubscriber map[pendingFlowRemoveDataKey]pendingFlowRemoveData
+}
+
+//OnuDevice represents ONU related info
+type OnuDevice struct {
+ deviceID string
+ deviceType string
+ serialNumber string
+ onuID uint32
+ intfID uint32
+ proxyDeviceID string
+ losRaised bool
+ rdiRaised bool
+}
+
+var pmNames = []string{
+ "rx_bytes",
+ "rx_packets",
+ "rx_mcast_packets",
+ "rx_bcast_packets",
+ "tx_bytes",
+ "tx_packets",
+ "tx_mcast_packets",
+ "tx_bcast_packets",
+}
+
+// set true if you want to disable BBSim function
+var disableBBSim bool = true
+
+//NewOnuDevice creates a new Onu Device
+func NewOnuDevice(devID, deviceTp, serialNum string, onuID, intfID uint32, proxyDevID string, losRaised bool) *OnuDevice {
+ var device OnuDevice
+ device.deviceID = devID
+ device.deviceType = deviceTp
+ device.serialNumber = serialNum
+ device.onuID = onuID
+ device.intfID = intfID
+ device.proxyDeviceID = proxyDevID
+ device.losRaised = losRaised
+ 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.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{}
+ dh.stopIndications = make(chan bool, 1)
+ dh.pendingFlowRemoveDataPerSubscriber = make(map[pendingFlowRemoveDataKey]pendingFlowRemoveData)
+
+ if dh.openOLT.config != nil {
+ NewL2oamHandle(context.Background(), dh.openOLT.config.InterfaceName, dh.openOLT.config.SrcMac)
+ }
+
+ return &dh
+}
+
+// start save the device to the data model
+func (dh *DeviceHandler) start(ctx context.Context) {
+ dh.lockDevice.Lock()
+ defer dh.lockDevice.Unlock()
+ logger.Debugw(ctx, "starting-device-agent", log.Fields{"device": dh.device})
+ logger.Debug(ctx, "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()
+ logger.Debug(ctx, "stopping-device-agent")
+ dh.exitChannel <- 1
+ logger.Debug(ctx, "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(ctx context.Context, host string) (string, error) {
+ var genmac string
+ var addr net.IP
+ var ips []string
+ var err error
+
+ logger.Debugw(ctx, "generating-mac-from-host", log.Fields{"host": host})
+
+ if addr = net.ParseIP(host); addr == nil {
+ logger.Debugw(ctx, "looking-up-hostname", log.Fields{"host": host})
+
+ if ips, err = net.LookupHost(host); err == nil {
+ logger.Debugw(ctx, "dns-result-ips", log.Fields{"ips": ips})
+ if addr = net.ParseIP(ips[0]); addr == nil {
+ return "", olterrors.NewErrInvalidValue(log.Fields{"ip": ips[0]}, nil)
+ }
+ genmac = macifyIP(addr)
+ logger.Debugw(ctx, "using-ip-as-mac",
+ log.Fields{"host": ips[0],
+ "mac": genmac})
+ return genmac, nil
+ }
+ return "", olterrors.NewErrAdapter("cannot-resolve-hostname-to-ip", log.Fields{"host": host}, err)
+ }
+
+ genmac = macifyIP(addr)
+ logger.Debugw(ctx, "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 "", olterrors.NewErrInvalidValue(log.Fields{"port-type": portType}, nil)
+}
+
+func (dh *DeviceHandler) addPort(ctx context.Context, 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 olterrors.NewErrNotFound("port-label", log.Fields{"port-number": portNum, "port-type": portType}, err)
+ }
+
+ if port, err := dh.coreProxy.GetDevicePort(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id, portNum); err == nil && port.Type == portType {
+ logger.Debug(ctx, "port-already-exists-updating-oper-status-of-port")
+ if err := dh.coreProxy.PortStateUpdate(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id, portType, portNum, operStatus); err != nil {
+ return olterrors.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
+ }
+ capacity := uint32(of.OfpPortFeatures_OFPPF_1GB_FD | of.OfpPortFeatures_OFPPF_FIBER)
+ port := &voltha.Port{
+ PortNo: portNum,
+ Label: label,
+ Type: portType,
+ OperStatus: operStatus,
+ 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),
+ },
+ }
+ logger.Debugw(ctx, "sending-port-update-to-core", log.Fields{"port": port})
+ if err := dh.coreProxy.PortCreated(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id, port); err != nil {
+ return olterrors.NewErrAdapter("error-creating-port", log.Fields{
+ "device-id": dh.device.Id,
+ "port-type": portType}, err)
+ }
+ go dh.updateLocalDevice(ctx)
+ return nil
+}
+
+func (dh *DeviceHandler) updateLocalDevice(ctx context.Context) {
+ dh.lockDevice.Lock()
+ defer dh.lockDevice.Unlock()
+ device, err := dh.coreProxy.GetDevice(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id, dh.device.Id)
+ if err != nil || device == nil {
+ logger.Errorf(ctx, "device-not-found", log.Fields{"device-id": dh.device.Id}, err)
+ return
+ }
+ dh.device = device
+}
+
+// nolint: gocyclo
+// readIndications to read the indications from the OLT device
+func (dh *DeviceHandler) readIndications(ctx context.Context) error {
+ defer logger.Debugw(ctx, "indications-ended", log.Fields{"device-id": dh.device.Id})
+ defer func() {
+ dh.lockDevice.Lock()
+ dh.isReadIndicationRoutineActive = false
+ dh.lockDevice.Unlock()
+ }()
+ indications, err := dh.startOpenOltIndicationStream(ctx)
+ if err != nil {
+ return err
+ }
+ if disableBBSim {
+ return nil
+ }
+ /* 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 olterrors.NewErrNotFound("device", log.Fields{"device-id": dh.device.Id}, err)
+ }
+
+ indicationBackoff := backoff.NewExponentialBackOff()
+ indicationBackoff.MaxElapsedTime = 0
+ indicationBackoff.MaxInterval = 1 * time.Minute
+
+ dh.lockDevice.Lock()
+ dh.isReadIndicationRoutineActive = true
+ dh.lockDevice.Unlock()
+
+Loop:
+ for {
+ select {
+ case <-dh.stopIndications:
+ logger.Debugw(ctx, "stopping-collecting-indications-for-olt", log.Fields{"deviceID:": dh.device.Id})
+ break Loop
+ default:
+ indication, err := indications.Recv()
+ if err == io.EOF {
+ logger.Infow(ctx, "eof-for-indications",
+ log.Fields{"err": err,
+ "device-id": dh.device.Id})
+ // 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.
+ logger.Warnw(ctx, "maximum-indication-backoff-reached--resetting-backoff-timer",
+ log.Fields{"max-indication-backoff": indicationBackoff.MaxElapsedTime,
+ "device-id": dh.device.Id})
+ indicationBackoff.Reset()
+ }
+
+ // On failure process a backoff timer while watching for stopIndications
+ // events
+ backoff := time.NewTimer(indicationBackoff.NextBackOff())
+ select {
+ case <-dh.stopIndications:
+ logger.Debugw(ctx, "stopping-collecting-indications-for-olt", log.Fields{"deviceID:": dh.device.Id})
+ if !backoff.Stop() {
+ <-backoff.C
+ }
+ break Loop
+ case <-backoff.C:
+ // backoff expired continue
+ }
+ if indications, err = dh.startOpenOltIndicationStream(ctx); err != nil {
+ return err
+ }
+ continue
+ }
+ if err != nil {
+ logger.Errorw(ctx, "read-indication-error",
+ log.Fields{"err": err,
+ "device-id": dh.device.Id})
+ if device.AdminState == voltha.AdminState_DELETED {
+ logger.Debug(ctx, "device-deleted--stopping-the-read-indication-thread")
+ break Loop
+ }
+ // Close the stream, and re-initialize it
+ if err = indications.CloseSend(); err != nil {
+ // Ok to ignore here, because we landed here due to a problem on the stream
+ // In all probability, the closeSend call may fail
+ logger.Debugw(ctx, "error-closing-send stream--error-ignored",
+ log.Fields{"err": err,
+ "device-id": dh.device.Id})
+ }
+ if indications, err = dh.startOpenOltIndicationStream(ctx); err != nil {
+ return err
+ }
+ // once we re-initialized the indication stream, continue to read indications
+ continue
+ }
+ // Reset backoff if we have a successful receive
+ indicationBackoff.Reset()
+ // When OLT is admin down, ignore all indications.
+ if device.AdminState == voltha.AdminState_DISABLED && !isIndicationAllowedDuringOltAdminDown(indication) {
+ logger.Debugw(ctx, "olt-is-admin-down, ignore indication",
+ log.Fields{"indication": indication,
+ "device-id": dh.device.Id})
+ continue
+ }
+ dh.handleIndication(ctx, indication)
+ }
+ }
+ _ = indications.CloseSend() // Ok to ignore error, as we stopping the readIndication anyway
+
+ return nil
+}
+
+func (dh *DeviceHandler) startOpenOltIndicationStream(ctx context.Context) (oop.Openolt_EnableIndicationClient, error) {
+ L2oamEnableIndication(ctx, dh)
+
+ indications, err := dh.Client.EnableIndication(ctx, new(oop.Empty))
+ if err != nil {
+ return nil, olterrors.NewErrCommunication("indication-read-failure", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ if indications == nil {
+ return nil, olterrors.NewErrInvalidValue(log.Fields{"indications": nil, "device-id": dh.device.Id}, nil).Log()
+ }
+
+ return indications, nil
+}
+
+// isIndicationAllowedDuringOltAdminDown returns true if the indication is allowed during OLT Admin down, else false
+func isIndicationAllowedDuringOltAdminDown(indication *oop.Indication) bool {
+ switch indication.Data.(type) {
+ case *oop.Indication_OltInd, *oop.Indication_IntfInd, *oop.Indication_IntfOperInd:
+ return true
+
+ default:
+ return false
+ }
+}
+
+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)
+ }
+ if err := dh.eventMgr.oltUpDownIndication(ctx, oltIndication, dh.device.Id, raisedTs); err != nil {
+ return olterrors.NewErrAdapter("failed-indication", log.Fields{
+ "device_id": dh.device.Id,
+ "indication": oltIndication,
+ "timestamp": raisedTs}, err)
+ }
+ 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:
+ span, ctx := log.CreateChildSpan(ctx, "olt-indication", log.Fields{"device-id": dh.device.Id})
+ defer span.Finish()
+
+ if err := dh.handleOltIndication(ctx, indication.GetOltInd()); err != nil {
+ _ = olterrors.NewErrAdapter("handle-indication-error", log.Fields{"type": "olt", "device-id": dh.device.Id}, err).Log()
+ }
+ case *oop.Indication_IntfInd:
+ span, ctx := log.CreateChildSpan(ctx, "interface-indication", log.Fields{"device-id": dh.device.Id})
+ defer span.Finish()
+
+ intfInd := indication.GetIntfInd()
+ go func() {
+ if err := dh.addPort(ctx, intfInd.GetIntfId(), voltha.Port_PON_OLT, intfInd.GetOperState()); err != nil {
+ _ = olterrors.NewErrAdapter("handle-indication-error", log.Fields{"type": "interface", "device-id": dh.device.Id}, err).Log()
+ }
+ }()
+ logger.Infow(ctx, "received-interface-indication", log.Fields{"InterfaceInd": intfInd, "device-id": dh.device.Id})
+ case *oop.Indication_IntfOperInd:
+ span, ctx := log.CreateChildSpan(ctx, "interface-oper-indication", log.Fields{"device-id": dh.device.Id})
+ defer span.Finish()
+
+ intfOperInd := indication.GetIntfOperInd()
+ if intfOperInd.GetType() == "nni" {
+ go func() {
+ if err := dh.addPort(ctx, intfOperInd.GetIntfId(), voltha.Port_ETHERNET_NNI, intfOperInd.GetOperState()); err != nil {
+ _ = olterrors.NewErrAdapter("handle-indication-error", log.Fields{"type": "interface-oper-nni", "device-id": dh.device.Id}, err).Log()
+ }
+ }()
+ if err := dh.resourceMgr.AddNNIToKVStore(ctx, intfOperInd.GetIntfId()); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } 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(ctx, intfOperInd.GetIntfId(), voltha.Port_PON_OLT, intfOperInd.GetOperState()); err != nil {
+ _ = olterrors.NewErrAdapter("handle-indication-error", log.Fields{"type": "interface-oper-pon", "device-id": dh.device.Id}, err).Log()
+ }
+ }()
+ go dh.eventMgr.oltIntfOperIndication(ctx, indication.GetIntfOperInd(), dh.device.Id, raisedTs)
+ }
+ logger.Infow(ctx, "received-interface-oper-indication",
+ log.Fields{"interfaceOperInd": intfOperInd,
+ "device-id": dh.device.Id})
+ case *oop.Indication_OnuDiscInd:
+ span, ctx := log.CreateChildSpan(ctx, "onu-discovery-indication", log.Fields{"device-id": dh.device.Id})
+ defer span.Finish()
+
+ onuDiscInd := indication.GetOnuDiscInd()
+ logger.Infow(ctx, "received-onu-discovery-indication", log.Fields{"OnuDiscInd": onuDiscInd, "device-id": dh.device.Id})
+ sn := dh.stringifySerialNumber(onuDiscInd.SerialNumber)
+ go func() {
+ if err := dh.onuDiscIndication(ctx, onuDiscInd, sn); err != nil {
+ _ = olterrors.NewErrAdapter("handle-indication-error", log.Fields{"type": "onu-discovery", "device-id": dh.device.Id}, err).Log()
+ }
+ }()
+ case *oop.Indication_OnuInd:
+ span, ctx := log.CreateChildSpan(ctx, "onu-indication", log.Fields{"device-id": dh.device.Id})
+ defer span.Finish()
+
+ onuInd := indication.GetOnuInd()
+ logger.Infow(ctx, "received-onu-indication", log.Fields{"OnuInd": onuInd, "device-id": dh.device.Id})
+ go func() {
+ if err := dh.onuIndication(ctx, onuInd); err != nil {
+ _ = olterrors.NewErrAdapter("handle-indication-error", log.Fields{"type": "onu", "device-id": dh.device.Id}, err).Log()
+ }
+ }()
+ case *oop.Indication_OmciInd:
+ span, ctx := log.CreateChildSpan(ctx, "omci-indication", log.Fields{"device-id": dh.device.Id})
+ defer span.Finish()
+
+ omciInd := indication.GetOmciInd()
+ logger.Debugw(ctx, "received-omci-indication", log.Fields{"intf-id": omciInd.IntfId, "onu-id": omciInd.OnuId, "device-id": dh.device.Id})
+ go func() {
+ if err := dh.omciIndication(ctx, omciInd); err != nil {
+ _ = olterrors.NewErrAdapter("handle-indication-error", log.Fields{"type": "omci", "device-id": dh.device.Id}, err).Log()
+ }
+ }()
+ case *oop.Indication_PktInd:
+ span, ctx := log.CreateChildSpan(ctx, "packet-indication", log.Fields{"device-id": dh.device.Id})
+ defer span.Finish()
+
+ pktInd := indication.GetPktInd()
+ logger.Debugw(ctx, "received-packet-indication", log.Fields{
+ "intf-type": pktInd.IntfId,
+ "intf-id": pktInd.IntfId,
+ "gem-port-id": pktInd.GemportId,
+ "port-no": pktInd.PortNo,
+ "device-id": dh.device.Id,
+ })
+
+ if logger.V(log.DebugLevel) {
+ logger.Debugw(ctx, "received-packet-indication-packet", log.Fields{
+ "intf-type": pktInd.IntfId,
+ "intf-id": pktInd.IntfId,
+ "gem-port-id": pktInd.GemportId,
+ "port-no": pktInd.PortNo,
+ "packet": hex.EncodeToString(pktInd.Pkt),
+ "device-id": dh.device.Id,
+ })
+ }
+
+ go func() {
+ if err := dh.handlePacketIndication(ctx, pktInd); err != nil {
+ _ = olterrors.NewErrAdapter("handle-indication-error", log.Fields{"type": "packet", "device-id": dh.device.Id}, err).Log()
+ }
+ }()
+ case *oop.Indication_PortStats:
+ span, ctx := log.CreateChildSpan(ctx, "port-statistics-indication", log.Fields{"device-id": dh.device.Id})
+ defer span.Finish()
+
+ portStats := indication.GetPortStats()
+ go dh.portStats.PortStatisticsIndication(ctx, portStats, dh.resourceMgr.DevInfo.GetPonPorts())
+ case *oop.Indication_FlowStats:
+ span, ctx := log.CreateChildSpan(ctx, "flow-stats-indication", log.Fields{"device-id": dh.device.Id})
+ defer span.Finish()
+
+ flowStats := indication.GetFlowStats()
+ logger.Infow(ctx, "received-flow-stats", log.Fields{"FlowStats": flowStats, "device-id": dh.device.Id})
+ case *oop.Indication_AlarmInd:
+ span, ctx := log.CreateChildSpan(ctx, "alarm-indication", log.Fields{"device-id": dh.device.Id})
+ defer span.Finish()
+
+ alarmInd := indication.GetAlarmInd()
+ logger.Infow(ctx, "received-alarm-indication", log.Fields{"AlarmInd": alarmInd, "device-id": dh.device.Id})
+ go dh.eventMgr.ProcessEvents(ctx, alarmInd, dh.device.Id, raisedTs)
+ }
+}
+
+// doStateUp handle the olt up indication and update to voltha core
+func (dh *DeviceHandler) doStateUp(ctx context.Context) error {
+ go startCollector(ctx, dh)
+
+ if err := dh.coreProxy.DeviceStateUpdate(ctx, dh.device.Id, voltha.ConnectStatus_REACHABLE,
+ voltha.OperStatus_ACTIVE); err != nil {
+ return olterrors.NewErrAdapter("device-update-failed", log.Fields{"device-id": dh.device.Id}, err)
+ }
+ return nil
+}
+
+// doStateDown handle the olt down indication
+func (dh *DeviceHandler) doStateDown(ctx context.Context) error {
+ dh.lockDevice.Lock()
+ defer dh.lockDevice.Unlock()
+ logger.Debugw(ctx, "do-state-down-start", log.Fields{"device-id": dh.device.Id})
+
+ device, err := dh.coreProxy.GetDevice(ctx, dh.device.Id, dh.device.Id)
+ if err != nil || device == nil {
+ /*TODO: needs to handle error scenarios */
+ return olterrors.NewErrNotFound("device", log.Fields{"device-id": dh.device.Id}, err)
+ }
+
+ cloned := proto.Clone(device).(*voltha.Device)
+
+ cloned.OperStatus = voltha.OperStatus_UNKNOWN
+ dh.device = cloned
+
+ if err = dh.coreProxy.DeviceStateUpdate(ctx, cloned.Id, cloned.ConnectStatus, cloned.OperStatus); err != nil {
+ return olterrors.NewErrAdapter("state-update-failed", log.Fields{"device-id": device.Id}, err)
+ }
+
+ onuDevices, err := dh.coreProxy.GetChildDevices(ctx, dh.device.Id)
+ if err != nil {
+ return olterrors.NewErrAdapter("child-device-fetch-failed", log.Fields{"device-id": dh.device.Id}, err)
+ }
+ 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 {
+ _ = olterrors.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{}
+ logger.Debugw(ctx, "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 {
+ if disableBBSim {
+ return nil
+ }
+ var err error
+ dh.clientCon, err = grpc.Dial(dh.device.GetHostAndPort(),
+ grpc.WithInsecure(),
+ grpc.WithBlock(),
+ grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(
+ grpc_opentracing.StreamClientInterceptor(grpc_opentracing.WithTracer(opentracing.GlobalTracer())),
+ )),
+ grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(
+ grpc_opentracing.UnaryClientInterceptor(grpc_opentracing.WithTracer(opentracing.GlobalTracer())),
+ )))
+
+ if err != nil {
+ return olterrors.NewErrCommunication("dial-failure", log.Fields{
+ "device-id": dh.device.Id,
+ "host-and-port": dh.device.GetHostAndPort()}, err)
+ }
+ return nil
+}
+
+// postInit create olt client instance to invoke RPC on the olt device
+func (dh *DeviceHandler) postInit(ctx context.Context) error {
+ if disableBBSim {
+ dh.Client = &mocks.MockOpenoltClient{}
+ } else {
+ 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 {
+ var err error
+ logger.Debugw(ctx, "olt-device-connected", log.Fields{"device-id": dh.device.Id})
+
+ device, err := dh.coreProxy.GetDevice(ctx, dh.device.Id, dh.device.Id)
+ if err != nil || device == nil {
+ /*TODO: needs to handle error scenarios */
+ return olterrors.NewErrAdapter("device-fetch-failed", log.Fields{"device-id": dh.device.Id}, err).LogAt(log.ErrorLevel)
+ }
+ if device.AdminState == voltha.AdminState_DISABLED {
+ logger.Debugln(ctx, "do-state-connected--device-admin-state-down")
+
+ cloned := proto.Clone(device).(*voltha.Device)
+ cloned.ConnectStatus = voltha.ConnectStatus_REACHABLE
+ cloned.OperStatus = voltha.OperStatus_UNKNOWN
+ dh.device = cloned
+ if err = dh.coreProxy.DeviceStateUpdate(ctx, cloned.Id, cloned.ConnectStatus, cloned.OperStatus); err != nil {
+ return olterrors.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 {
+ return olterrors.NewErrAdapter("olt-disable-failed", log.Fields{"device-id": dh.device.Id}, err).LogAt(log.ErrorLevel)
+ }
+ // We should still go ahead an initialize various device handler modules so that when OLT is re-enabled, we have
+ // all the modules initialized and ready to handle incoming ONUs.
+
+ err = dh.initializeDeviceHandlerModules(ctx)
+ if err != nil {
+ return olterrors.NewErrAdapter("device-handler-initialization-failed", log.Fields{"device-id": dh.device.Id}, err).LogAt(log.ErrorLevel)
+ }
+
+ // Start reading indications
+ go func() {
+ if err = dh.readIndications(ctx); err != nil {
+ _ = olterrors.NewErrAdapter("indication-read-failure", log.Fields{"device-id": dh.device.Id}, err).LogAt(log.ErrorLevel)
+ }
+ }()
+ return nil
+ }
+
+ ports, err := dh.coreProxy.ListDevicePorts(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id)
+ if err != nil {
+ /*TODO: needs to handle error scenarios */
+ return olterrors.NewErrAdapter("fetch-ports-failed", log.Fields{"device-id": dh.device.Id}, err)
+ }
+ dh.populateActivePorts(ctx, ports)
+ if err := dh.disableAdminDownPorts(ctx, ports); err != nil {
+ return olterrors.NewErrAdapter("port-status-update-failed", log.Fields{"ports": ports}, err)
+ }
+
+ if err := dh.initializeDeviceHandlerModules(ctx); err != nil {
+ return olterrors.NewErrAdapter("device-handler-initialization-failed", log.Fields{"device-id": dh.device.Id}, err).LogAt(log.ErrorLevel)
+ }
+
+ go func() {
+ if err := dh.readIndications(ctx); err != nil {
+ _ = olterrors.NewErrAdapter("read-indications-failure", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ }()
+ go dh.updateLocalDevice(ctx)
+
+ if device.PmConfigs != nil {
+ dh.UpdatePmConfig(ctx, device.PmConfigs)
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) initializeDeviceHandlerModules(ctx context.Context) error {
+ deviceInfo, err := dh.populateDeviceInfo(ctx)
+
+ if err != nil {
+ return olterrors.NewErrAdapter("populate-device-info-failed", log.Fields{"device-id": dh.device.Id}, err)
+ }
+ if dh.resourceMgr = rsrcMgr.NewResourceMgr(ctx, dh.device.Id, dh.openOLT.KVStoreAddress, dh.openOLT.KVStoreType, dh.device.Type, deviceInfo); dh.resourceMgr == nil {
+ return olterrors.ErrResourceManagerInstantiating
+ }
+
+ if dh.flowMgr = NewFlowManager(ctx, dh, dh.resourceMgr); dh.flowMgr == nil {
+ return olterrors.ErrResourceManagerInstantiating
+
+ }
+ /* TODO: Instantiate Alarm , stats , BW managers */
+ /* Instantiating Event Manager to handle Alarms and KPIs */
+ dh.eventMgr = NewEventMgr(dh.EventProxy, dh)
+
+ dh.portStats = NewOpenOltStatsMgr(ctx, dh)
+
+ return nil
+
+}
+
+func (dh *DeviceHandler) populateDeviceInfo(ctx context.Context) (*oop.DeviceInfo, error) {
+ var err error
+ var deviceInfo *oop.DeviceInfo
+
+ if disableBBSim {
+ deviceInfo, err = L2oamGetDeviceInfo(log.WithSpanFromContext(context.Background(), ctx), dh)
+ } else {
+ deviceInfo, err = dh.Client.GetDeviceInfo(log.WithSpanFromContext(context.Background(), ctx), new(oop.Empty))
+ }
+
+ if err != nil {
+ return nil, olterrors.NewErrPersistence("get", "device", 0, nil, err)
+ }
+ if deviceInfo == nil {
+ return nil, olterrors.NewErrInvalidValue(log.Fields{"device": nil}, nil)
+ }
+
+ logger.Debugw(ctx, "fetched-device-info", log.Fields{"deviceInfo": deviceInfo, "device-id": dh.device.Id})
+ 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 == "" {
+ logger.Warnw(ctx, "no-device-id-provided-using-host", log.Fields{"hostport": dh.device.GetHostAndPort()})
+ host := strings.Split(dh.device.GetHostAndPort(), ":")[0]
+ genmac, err := generateMacFromHost(ctx, host)
+ if err != nil {
+ return nil, olterrors.NewErrAdapter("failed-to-generate-mac-host", log.Fields{"host": host}, err)
+ }
+ logger.Debugw(ctx, "using-host-for-mac-address", log.Fields{"host": host, "mac": genmac})
+ dh.device.MacAddress = genmac
+ } else {
+ dh.device.MacAddress = deviceInfo.DeviceId
+ }
+
+ if err := dh.coreProxy.DeviceUpdate(log.WithSpanFromContext(context.TODO(), ctx), dh.device); err != nil {
+ return nil, olterrors.NewErrAdapter("device-update-failed", log.Fields{"device-id": dh.device.Id}, err)
+ }
+
+ return deviceInfo, nil
+}
+
+func startCollector(ctx context.Context, dh *DeviceHandler) {
+ logger.Debugf(ctx, "starting-collector")
+ for {
+ select {
+ case <-dh.stopCollector:
+ logger.Debugw(ctx, "stopping-collector-for-olt", log.Fields{"deviceID:": dh.device.Id})
+ return
+ case <-time.After(time.Duration(dh.metrics.ToPmConfigs().DefaultFreq) * time.Second):
+
+ ports, err := dh.coreProxy.ListDevicePorts(log.WithSpanFromContext(context.Background(), ctx), dh.device.Id)
+ if err != nil {
+ logger.Warnw(ctx, "failed-to-list-ports", log.Fields{"device-id": dh.device.Id, "error": err})
+ continue
+ }
+ for _, port := range ports {
+ // NNI Stats
+ if port.Type == voltha.Port_ETHERNET_NNI {
+ intfID := PortNoToIntfID(port.PortNo, voltha.Port_ETHERNET_NNI)
+ cmnni := dh.portStats.collectNNIMetrics(intfID)
+ logger.Debugw(ctx, "collect-nni-metrics", log.Fields{"metrics": cmnni})
+ go dh.portStats.publishMetrics(ctx, cmnni, port, dh.device.Id, dh.device.Type)
+ logger.Debugw(ctx, "publish-nni-metrics", log.Fields{"nni-port": port.Label})
+ }
+ // PON Stats
+ if port.Type == voltha.Port_PON_OLT {
+ intfID := PortNoToIntfID(port.PortNo, voltha.Port_PON_OLT)
+ if val, ok := dh.activePorts.Load(intfID); ok && val == true {
+ cmpon := dh.portStats.collectPONMetrics(intfID)
+ logger.Debugw(ctx, "collect-pon-metrics", log.Fields{"metrics": cmpon})
+ go dh.portStats.publishMetrics(ctx, cmpon, port, dh.device.Id, dh.device.Type)
+ }
+ logger.Debugw(ctx, "publish-pon-metrics", log.Fields{"pon-port": port.Label})
+ }
+ }
+ }
+ }
+}
+
+//AdoptDevice adopts the OLT device
+func (dh *DeviceHandler) AdoptDevice(ctx context.Context, device *voltha.Device) {
+ dh.transitionMap = NewTransitionMap(dh)
+ logger.Infow(ctx, "adopt-device", log.Fields{"device-id": device.Id, "Address": device.GetHostAndPort()})
+ dh.transitionMap.Handle(ctx, DeviceInit)
+
+ if err := dh.coreProxy.DevicePMConfigUpdate(ctx, dh.metrics.ToPmConfigs()); err != nil {
+ _ = olterrors.NewErrAdapter("error-updating-performance-metrics", log.Fields{"device-id": device.Id}, err).LogAt(log.ErrorLevel)
+ }
+
+ go startHeartbeatCheck(ctx, 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
+}
+
+func (dh *DeviceHandler) omciIndication(ctx context.Context, omciInd *oop.OmciIndication) error {
+ logger.Debugw(ctx, "omci-indication", log.Fields{"intf-id": omciInd.IntfId, "onu-id": omciInd.OnuId, "device-id": dh.device.Id})
+ var deviceType string
+ var deviceID string
+ var proxyDeviceID string
+
+ transid := extractOmciTransactionID(omciInd.Pkt)
+ if logger.V(log.DebugLevel) {
+ logger.Debugw(ctx, "recv-omci-msg", log.Fields{"intf-id": omciInd.IntfId, "onu-id": omciInd.OnuId, "device-id": dh.device.Id,
+ "omci-transaction-id": transid, "omci-msg": hex.EncodeToString(omciInd.Pkt)})
+ }
+
+ onuKey := dh.formOnuKey(omciInd.IntfId, omciInd.OnuId)
+
+ if onuInCache, ok := dh.onus.Load(onuKey); !ok {
+
+ logger.Debugw(ctx, "omci-indication-for-a-device-not-in-cache.", log.Fields{"intf-id": omciInd.IntfId, "onu-id": omciInd.OnuId, "device-id": dh.device.Id})
+ 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(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id, kwargs)
+ if err != nil {
+ return olterrors.NewErrNotFound("onu", log.Fields{
+ "intf-id": omciInd.IntfId,
+ "onu-id": omciInd.OnuId}, err)
+ }
+ 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, false))
+ } else {
+ //found in cache
+ logger.Debugw(ctx, "omci-indication-for-a-device-in-cache.", log.Fields{"intf-id": omciInd.IntfId, "onu-id": omciInd.OnuId, "device-id": dh.device.Id})
+ deviceType = onuInCache.(*OnuDevice).deviceType
+ deviceID = onuInCache.(*OnuDevice).deviceID
+ proxyDeviceID = onuInCache.(*OnuDevice).proxyDeviceID
+ }
+
+ omciMsg := &ic.InterAdapterOmciMessage{Message: omciInd.Pkt}
+ if err := dh.AdapterProxy.SendInterAdapterMessage(log.WithSpanFromContext(context.Background(), ctx), omciMsg,
+ ic.InterAdapterMessageType_OMCI_REQUEST, dh.device.Type, deviceType,
+ deviceID, proxyDeviceID, ""); err != nil {
+ return olterrors.NewErrCommunication("omci-request", log.Fields{
+ "source": dh.device.Type,
+ "destination": deviceType,
+ "onu-id": deviceID,
+ "proxy-device-id": proxyDeviceID}, err)
+ }
+ 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(ctx context.Context, msg *ic.InterAdapterMessage) error {
+ logger.Debugw(ctx, "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
+
+ logger.Debugw(ctx, "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 {
+ return olterrors.NewErrAdapter("cannot-unmarshal-omci-msg-body", log.Fields{"msgbody": msgBody}, err)
+ }
+
+ if omciMsg.GetProxyAddress() == nil {
+ onuDevice, err := dh.coreProxy.GetDevice(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id, toDeviceID)
+ if err != nil {
+ return olterrors.NewErrNotFound("onu", log.Fields{
+ "device-id": dh.device.Id,
+ "onu-device-id": toDeviceID}, err)
+ }
+ logger.Debugw(ctx, "device-retrieved-from-core", log.Fields{"msgID": msgID, "fromTopic": fromTopic, "toTopic": toTopic, "toDeviceID": toDeviceID, "proxyDeviceID": proxyDeviceID})
+ if err := dh.sendProxiedMessage(ctx, onuDevice, omciMsg); err != nil {
+ return olterrors.NewErrCommunication("send-failed", log.Fields{
+ "device-id": dh.device.Id,
+ "onu-device-id": toDeviceID}, err)
+ }
+ } else {
+ logger.Debugw(ctx, "proxy-address-found-in-omci-message", log.Fields{"msgID": msgID, "fromTopic": fromTopic, "toTopic": toTopic, "toDeviceID": toDeviceID, "proxyDeviceID": proxyDeviceID})
+ if err := dh.sendProxiedMessage(ctx, nil, omciMsg); err != nil {
+ return olterrors.NewErrCommunication("send-failed", log.Fields{
+ "device-id": dh.device.Id,
+ "onu-device-id": toDeviceID}, err)
+ }
+ }
+ } else if msg.Header.Type == ic.InterAdapterMessageType_ONU_IND_REQUEST {
+ logger.Debugw(ctx, "got-message-from-onu", log.Fields{"message": msg.Header.Id})
+ if err := dh.receivedMsgFromOnu(ctx, msg); err != nil {
+ return err
+ }
+ } else {
+ return olterrors.NewErrInvalidValue(log.Fields{"inter-adapter-message-type": msg.Header.Type}, nil)
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) sendProxiedMessage(ctx context.Context, 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 {
+ logger.Debugw(ctx, "onu-not-reachable--cannot-send-omci", log.Fields{"intf-id": intfID, "onu-id": onuID})
+
+ return olterrors.NewErrCommunication("unreachable", log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID}, nil)
+ }
+
+ var omciMessage *oop.OmciMsg
+ hexPkt := make([]byte, hex.EncodedLen(len(omciMsg.Message)))
+ hex.Encode(hexPkt, omciMsg.Message)
+ omciMessage = &oop.OmciMsg{IntfId: intfID, OnuId: onuID, Pkt: hexPkt}
+
+ transid := extractOmciTransactionID(omciMsg.Message)
+ logger.Debugw(ctx, "sent-omci-msg", log.Fields{"intf-id": intfID, "onu-id": onuID,
+ "omciTransactionID": transid, "omciMsg": string(omciMessage.Pkt)})
+
+ _, err := dh.Client.OmciMsgOut(log.WithSpanFromContext(context.Background(), ctx), omciMessage)
+ if err != nil {
+ return olterrors.NewErrCommunication("omci-send-failed", log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "message": omciMessage}, err)
+ }
+
+ if disableBBSim {
+ if err = dh.fakeOmciIndication(ctx, intfID, onuID, omciMsg.Message); err != nil {
+ return nil
+ }
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) activateONU(ctx context.Context, intfID uint32, onuID int64, serialNum *oop.SerialNumber, serialNumber string) error {
+ logger.Debugw(ctx, "activate-onu", log.Fields{"intf-id": intfID, "onu-id": onuID, "serialNum": serialNum, "serialNumber": serialNumber, "device-id": dh.device.Id})
+ if err := dh.flowMgr.UpdateOnuInfo(ctx, intfID, uint32(onuID), serialNumber); err != nil {
+ return olterrors.NewErrAdapter("onu-activate-failed", log.Fields{"onu": onuID, "intf-id": intfID}, err)
+ }
+ 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 {
+ logger.Debugw(ctx, "onu-activation-in-progress", log.Fields{"SerialNumber": serialNumber, "onu-id": onuID, "device-id": dh.device.Id})
+
+ } else {
+ return olterrors.NewErrAdapter("onu-activate-failed", log.Fields{"onu": Onu, "device-id": dh.device.Id}, err)
+ }
+ } else {
+ logger.Infow(ctx, "activated-onu", log.Fields{"SerialNumber": serialNumber, "device-id": dh.device.Id})
+ }
+ 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)
+
+ logger.Infow(ctx, "new-discovery-indication", log.Fields{"sn": sn})
+
+ kwargs := make(map[string]interface{})
+ if sn != "" {
+ kwargs["serial_number"] = sn
+ } else {
+ return olterrors.NewErrInvalidValue(log.Fields{"serial-number": sn}, nil)
+ }
+
+ var alarmInd oop.OnuAlarmIndication
+ raisedTs := time.Now().UnixNano()
+ if _, loaded := dh.discOnus.LoadOrStore(sn, true); loaded {
+
+ /* When PON cable disconnected and connected back from OLT, it was expected OnuAlarmIndication
+ with "los_status: off" should be raised but BAL does not raise this Alarm hence manually sending
+ OnuLosClear event on receiving OnuDiscoveryIndication for the Onu after checking whether
+ OnuLosRaise event sent for it */
+ dh.onus.Range(func(Onukey interface{}, onuInCache interface{}) bool {
+ if onuInCache.(*OnuDevice).serialNumber == sn && onuInCache.(*OnuDevice).losRaised {
+ if onuDiscInd.GetIntfId() != onuInCache.(*OnuDevice).intfID {
+ logger.Warnw(ctx, "onu-is-on-a-different-intf-id-now", log.Fields{
+ "previousIntfId": onuInCache.(*OnuDevice).intfID,
+ "currentIntfId": onuDiscInd.GetIntfId()})
+ // TODO:: Should we need to ignore raising OnuLosClear event
+ // when onu connected to different PON?
+ }
+ alarmInd.IntfId = onuInCache.(*OnuDevice).intfID
+ alarmInd.OnuId = onuInCache.(*OnuDevice).onuID
+ alarmInd.LosStatus = statusCheckOff
+ go func() {
+ if err := dh.eventMgr.onuAlarmIndication(ctx, &alarmInd, onuInCache.(*OnuDevice).deviceID, raisedTs); err != nil {
+ logger.Debugw(ctx, "indication-failed", log.Fields{"error": err})
+ }
+ }()
+ }
+ return true
+ })
+
+ logger.Warnw(ctx, "onu-sn-is-already-being-processed", log.Fields{"sn": sn})
+ return nil
+ }
+
+ var onuID uint32
+
+ onuDevice, err := dh.coreProxy.GetChildDevice(ctx, dh.device.Id, kwargs)
+
+ if err != nil {
+ logger.Debugw(ctx, "core-proxy-get-child-device-failed", log.Fields{"parentDevice": dh.device.Id, "err": err, "sn": sn})
+ if e, ok := status.FromError(err); ok {
+ logger.Debugw(ctx, "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 olterrors.NewErrTimeout("get-child-device", log.Fields{"device-id": dh.device.Id}, err)
+ }
+ }
+ }
+
+ if onuDevice == nil {
+ // NOTE this should happen a single time, and only if GetChildDevice returns NotFound
+ logger.Debugw(ctx, "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()
+
+ logger.Infow(ctx, "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 olterrors.NewErrAdapter("resource-manager-get-onu-id-failed", log.Fields{
+ "pon-intf-id": ponintfid,
+ "serial-number": sn}, err)
+ }
+
+ if onuDevice, err = dh.coreProxy.ChildDeviceDetected(log.WithSpanFromContext(context.TODO(), ctx), 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 olterrors.NewErrAdapter("core-proxy-child-device-detected-failed", log.Fields{
+ "pon-intf-id": ponintfid,
+ "serial-number": sn}, err)
+ }
+ if err := dh.eventMgr.OnuDiscoveryIndication(ctx, onuDiscInd, dh.device.Id, onuDevice.Id, onuID, sn, time.Now().UnixNano()); err != nil {
+ logger.Warnw(ctx, "discovery-indication-failed", log.Fields{"error": err})
+ }
+ logger.Infow(ctx, "onu-child-device-added",
+ log.Fields{"onuDevice": onuDevice,
+ "sn": sn,
+ "onu-id": onuID,
+ "device-id": dh.device.Id})
+ }
+
+ onuID = onuDevice.ProxyAddress.OnuId
+ logger.Debugw(ctx, "onu-discovery-indication-key-create",
+ log.Fields{"onu-id": 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, false)
+ dh.onus.Store(onuKey, onuDev)
+ logger.Debugw(ctx, "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 olterrors.NewErrAdapter("failed-to-update-device-state", log.Fields{
+ "device-id": onuDevice.Id,
+ "serial-number": sn}, err)
+ }
+ logger.Infow(ctx, "onu-discovered-reachable", log.Fields{"device-id": onuDevice.Id, "sn": sn})
+ if err := dh.activateONU(ctx, onuDiscInd.IntfId, int64(onuID), onuDiscInd.SerialNumber, sn); err != nil {
+ return olterrors.NewErrAdapter("onu-activation-failed", log.Fields{
+ "device-id": onuDevice.Id,
+ "serial-number": sn}, err)
+ }
+ return nil
+}
+func (dh *DeviceHandler) onuDiscIndication2(ctx context.Context, onuDiscInd *oop.OnuDiscIndication, sn string, macAddress string) error {
+ channelID := onuDiscInd.GetIntfId()
+ parentPortNo := IntfIDToPortNo(onuDiscInd.GetIntfId(), voltha.Port_PON_OLT)
+
+ logger.Infow(ctx, "new-discovery-indication", log.Fields{"sn": sn})
+
+ kwargs := make(map[string]interface{})
+ if sn != "" {
+ kwargs["serial_number"] = sn
+ } else {
+ return olterrors.NewErrInvalidValue(log.Fields{"serial-number": sn}, nil)
+ }
+
+ var alarmInd oop.OnuAlarmIndication
+ raisedTs := time.Now().UnixNano()
+ if _, loaded := dh.discOnus.LoadOrStore(sn, true); loaded {
+
+ /* When PON cable disconnected and connected back from OLT, it was expected OnuAlarmIndication
+ with "los_status: off" should be raised but BAL does not raise this Alarm hence manually sending
+ OnuLosClear event on receiving OnuDiscoveryIndication for the Onu after checking whether
+ OnuLosRaise event sent for it */
+ dh.onus.Range(func(Onukey interface{}, onuInCache interface{}) bool {
+ if onuInCache.(*OnuDevice).serialNumber == sn && onuInCache.(*OnuDevice).losRaised {
+ if onuDiscInd.GetIntfId() != onuInCache.(*OnuDevice).intfID {
+ logger.Warnw(ctx, "onu-is-on-a-different-intf-id-now", log.Fields{
+ "previousIntfId": onuInCache.(*OnuDevice).intfID,
+ "currentIntfId": onuDiscInd.GetIntfId()})
+ // TODO:: Should we need to ignore raising OnuLosClear event
+ // when onu connected to different PON?
+ }
+ alarmInd.IntfId = onuInCache.(*OnuDevice).intfID
+ alarmInd.OnuId = onuInCache.(*OnuDevice).onuID
+ alarmInd.LosStatus = statusCheckOff
+ go func() {
+ if err := dh.eventMgr.onuAlarmIndication(ctx, &alarmInd, onuInCache.(*OnuDevice).deviceID, raisedTs); err != nil {
+ logger.Debugw(ctx, "indication-failed", log.Fields{"error": err})
+ }
+ }()
+ }
+ return true
+ })
+
+ logger.Warnw(ctx, "onu-sn-is-already-being-processed", log.Fields{"sn": sn})
+ return nil
+ }
+
+ var onuID uint32
+
+ onuDevice, err := dh.coreProxy.GetChildDevice(ctx, dh.device.Id, kwargs)
+
+ if err != nil {
+ logger.Debugw(ctx, "core-proxy-get-child-device-failed", log.Fields{"parentDevice": dh.device.Id, "err": err, "sn": sn})
+ if e, ok := status.FromError(err); ok {
+ logger.Debugw(ctx, "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 olterrors.NewErrTimeout("get-child-device", log.Fields{"device-id": dh.device.Id}, err)
+ }
+ }
+ }
+
+ if onuDevice == nil {
+ // NOTE this should happen a single time, and only if GetChildDevice returns NotFound
+ logger.Debugw(ctx, "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()
+
+ logger.Infow(ctx, "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 olterrors.NewErrAdapter("resource-manager-get-onu-id-failed", log.Fields{
+ "pon-intf-id": ponintfid,
+ "serial-number": sn}, err)
+ }
+
+ if onuDevice, err = dh.coreProxy.ChildDeviceDetected(log.WithSpanFromContext(context.TODO(), ctx), 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 olterrors.NewErrAdapter("core-proxy-child-device-detected-failed", log.Fields{
+ "pon-intf-id": ponintfid,
+ "serial-number": sn}, err)
+ }
+ if err := dh.eventMgr.OnuDiscoveryIndication(ctx, onuDiscInd, dh.device.Id, onuDevice.Id, onuID, sn, time.Now().UnixNano()); err != nil {
+ logger.Warnw(ctx, "discovery-indication-failed", log.Fields{"error": err})
+ }
+ logger.Infow(ctx, "onu-child-device-added",
+ log.Fields{"onuDevice": onuDevice,
+ "sn": sn,
+ "onu-id": onuID,
+ "device-id": dh.device.Id})
+ }
+
+ onuID = onuDevice.ProxyAddress.OnuId
+ logger.Debugw(ctx, "onu-discovery-indication-key-create",
+ log.Fields{"onu-id": 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, false)
+ dh.onus.Store(onuKey, onuDev)
+ logger.Debugw(ctx, "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 olterrors.NewErrAdapter("failed-to-update-device-state", log.Fields{
+ "device-id": onuDevice.Id,
+ "serial-number": sn}, err)
+ }
+ logger.Infow(ctx, "onu-discovered-reachable", log.Fields{"device-id": onuDevice.Id, "sn": sn})
+ if err := dh.activateONU(ctx, onuDiscInd.IntfId, int64(onuID), onuDiscInd.SerialNumber, sn); err != nil {
+ return olterrors.NewErrAdapter("onu-activation-failed", log.Fields{
+ "device-id": onuDevice.Id,
+ "serial-number": sn}, err)
+ }
+
+ device := FindL2oamDevice(macAddress)
+ if device != nil {
+ logger.Debug(ctx, fmt.Sprintf("onuDiscIndication2() onu.initialize() called. deviceId=%s, onuId=%d", onuDevice.Id, onuID))
+ onu := device.(*L2oamOnuDevice)
+ onu.update(macAddress, onuDevice.Id, onuID)
+ onu.setActiveState(ctx, true)
+ }
+ onuDevice.MacAddress = macAddress
+ onuDevice.Vendor = string(onuDiscInd.SerialNumber.VendorSpecific)
+ if err := dh.coreProxy.DeviceUpdate(log.WithSpanFromContext(context.TODO(), ctx), onuDevice); err != nil {
+ logger.Error(ctx, fmt.Sprintf("onuDiscIndication2() DeviceUpdate() failed. macAddress=%s, deviceId=%s", macAddress, onuDevice.Id))
+ }
+
+ return nil
+}
+func (dh *DeviceHandler) onuIndication(ctx context.Context, 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
+ logger.Debugw(ctx, "onu-indication-key-create",
+ log.Fields{"onuId": onuInd.OnuId,
+ "intfId": onuInd.GetIntfId(),
+ "device-id": dh.device.Id})
+ 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(ctx, 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(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id, kwargs)
+ }
+
+ if err != nil || onuDevice == nil {
+ return olterrors.NewErrNotFound("onu-device", errFields, err)
+ }
+
+ if onuDevice.ParentPortNo != ponPort {
+ logger.Warnw(ctx, "onu-is-on-a-different-intf-id-now", log.Fields{
+ "previousIntfId": onuDevice.ParentPortNo,
+ "currentIntfId": ponPort})
+ }
+
+ if onuDevice.ProxyAddress.OnuId != onuInd.OnuId {
+ logger.Warnw(ctx, "onu-id-mismatch-possible-if-voltha-and-olt-rebooted", log.Fields{
+ "expected-onu-id": onuDevice.ProxyAddress.OnuId,
+ "received-onu-id": onuInd.OnuId,
+ "device-id": dh.device.Id})
+ }
+ 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, false))
+
+ }
+ if err := dh.updateOnuStates(ctx, onuDevice, onuInd); err != nil {
+ return olterrors.NewErrCommunication("state-update-failed", errFields, err)
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) updateOnuStates(ctx context.Context, onuDevice *voltha.Device, onuInd *oop.OnuIndication) error {
+ logger.Debugw(ctx, "onu-indication-for-state", log.Fields{"onuIndication": onuInd, "device-id": onuDevice.Id, "operStatus": onuDevice.OperStatus, "adminStatus": onuDevice.AdminState})
+ if onuInd.AdminState == "down" || onuInd.OperState == "down" {
+ // The ONU has gone admin_state "down" or oper_state "down" - we expect the ONU to send discovery again
+ // The ONU admin_state is "up" while "oper_state" is down in cases where ONU activation fails. In this case
+ // the ONU sends Discovery again.
+ dh.discOnus.Delete(onuDevice.SerialNumber)
+ // Tests have shown that we sometimes get OperState as NOT down even if AdminState is down, forcing it
+ if onuInd.OperState != "down" {
+ logger.Warnw(ctx, "onu-admin-state-down", log.Fields{"operState": onuInd.OperState})
+ onuInd.OperState = "down"
+ }
+ }
+
+ switch onuInd.OperState {
+ case "down":
+ logger.Debugw(ctx, "sending-interadapter-onu-indication", log.Fields{"onuIndication": onuInd, "device-id": 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 olterrors.NewErrCommunication("inter-adapter-send-failed", log.Fields{
+ "onu-indicator": onuInd,
+ "source": "openolt",
+ "device-type": onuDevice.Type,
+ "device-id": onuDevice.Id}, err)
+ }
+ case "up":
+ logger.Debugw(ctx, "sending-interadapter-onu-indication", log.Fields{"onuIndication": onuInd, "device-id": 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 olterrors.NewErrCommunication("inter-adapter-send-failed", log.Fields{
+ "onu-indicator": onuInd,
+ "source": "openolt",
+ "device-type": onuDevice.Type,
+ "device-id": onuDevice.Id}, err)
+ }
+ default:
+ return olterrors.NewErrInvalidValue(log.Fields{"oper-state": onuInd.OperState}, nil)
+ }
+ 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 olterrors.ErrNotImplemented
+}
+
+//GetChildDevice returns the child device for given parent port and onu id
+func (dh *DeviceHandler) GetChildDevice(ctx context.Context, parentPort, onuID uint32) (*voltha.Device, error) {
+ logger.Debugw(ctx, "getchilddevice",
+ log.Fields{"pon-port": parentPort,
+ "onu-id": onuID,
+ "device-id": dh.device.Id})
+ kwargs := make(map[string]interface{})
+ kwargs["onu_id"] = onuID
+ kwargs["parent_port_no"] = parentPort
+ onuDevice, err := dh.coreProxy.GetChildDevice(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id, kwargs)
+ if err != nil {
+ return nil, olterrors.NewErrNotFound("onu-device", log.Fields{
+ "intf-id": parentPort,
+ "onu-id": onuID}, err)
+ }
+ logger.Debugw(ctx, "successfully-received-child-device-from-core", log.Fields{"child-device-id": onuDevice.Id, "child-device-sn": onuDevice.SerialNumber})
+ 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(ctx context.Context, logicalPort uint32, packetPayload []byte) error {
+ if logger.V(log.DebugLevel) {
+ logger.Debugw(ctx, "send-packet-in-to-core", log.Fields{
+ "port": logicalPort,
+ "packet": hex.EncodeToString(packetPayload),
+ "device-id": dh.device.Id,
+ })
+ }
+ if err := dh.coreProxy.SendPacketIn(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id, logicalPort, packetPayload); err != nil {
+ return olterrors.NewErrCommunication("packet-send-failed", log.Fields{
+ "source": "adapter",
+ "destination": "core",
+ "device-id": dh.device.Id,
+ "logical-port": logicalPort,
+ "packet": hex.EncodeToString(packetPayload)}, err)
+ }
+ if logger.V(log.DebugLevel) {
+ logger.Debugw(ctx, "sent-packet-in-to-core-successfully", log.Fields{
+ "packet": hex.EncodeToString(packetPayload),
+ "device-id": dh.device.Id,
+ })
+ }
+ return nil
+}
+
+// UpdatePmConfig updates the pm metrics.
+func (dh *DeviceHandler) UpdatePmConfig(ctx context.Context, pmConfigs *voltha.PmConfigs) {
+ logger.Infow(ctx, "update-pm-configs", log.Fields{"device-id": dh.device.Id, "pm-configs": pmConfigs})
+
+ if pmConfigs.DefaultFreq != dh.metrics.ToPmConfigs().DefaultFreq {
+ dh.metrics.UpdateFrequency(pmConfigs.DefaultFreq)
+ logger.Debugf(ctx, "frequency-updated")
+ }
+
+ if !pmConfigs.Grouped {
+ metrics := dh.metrics.GetSubscriberMetrics()
+ for _, m := range pmConfigs.Metrics {
+ metrics[m.Name].Enabled = m.Enabled
+
+ }
+ }
+}
+
+//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 {
+ logger.Debugw(ctx, "received-incremental-flowupdate-in-device-handler", log.Fields{"device-id": device.Id, "flows": flows, "groups": groups, "flowMetadata": flowMetadata})
+
+ var errorsList []error
+
+ if flows != nil {
+ for _, flow := range flows.ToRemove.Items {
+ dh.incrementActiveFlowRemoveCount(ctx, flow)
+
+ logger.Debugw(ctx, "removing-flow",
+ log.Fields{"device-id": device.Id,
+ "flowToRemove": flow})
+ err := dh.flowMgr.RemoveFlow(ctx, flow)
+ if err != nil {
+ errorsList = append(errorsList, err)
+ }
+
+ dh.decrementActiveFlowRemoveCount(ctx, flow)
+ }
+
+ for _, flow := range flows.ToAdd.Items {
+ logger.Debugw(ctx, "adding-flow",
+ log.Fields{"device-id": device.Id,
+ "flowToAdd": flow})
+ // If there are active Flow Remove in progress for a given subscriber, wait until it completes
+ dh.waitForFlowRemoveToFinish(ctx, flow)
+ err := dh.flowMgr.AddFlow(ctx, flow, flowMetadata)
+ if err != nil {
+ errorsList = append(errorsList, err)
+ }
+ }
+ }
+
+ if groups != nil {
+ for _, group := range groups.ToAdd.Items {
+ err := dh.flowMgr.AddGroup(ctx, group)
+ if err != nil {
+ errorsList = append(errorsList, err)
+ }
+ }
+ for _, group := range groups.ToUpdate.Items {
+ err := dh.flowMgr.ModifyGroup(ctx, group)
+ if err != nil {
+ errorsList = append(errorsList, err)
+ }
+ }
+ for _, group := range groups.ToRemove.Items {
+ err := dh.flowMgr.DeleteGroup(ctx, group)
+ if err != nil {
+ errorsList = append(errorsList, err)
+ }
+ }
+ }
+ if len(errorsList) > 0 {
+ return fmt.Errorf("errors-installing-flows-groups, errors:%v", errorsList)
+ }
+ logger.Debugw(ctx, "updated-flows-incrementally-successfully", log.Fields{"device-id": dh.device.Id})
+ 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(ctx context.Context, 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*/
+ if dh.Client != nil {
+ if _, err := dh.Client.DisableOlt(log.WithSpanFromContext(context.Background(), ctx), new(oop.Empty)); err != nil {
+ if e, ok := status.FromError(err); ok && e.Code() == codes.Internal {
+ return olterrors.NewErrAdapter("olt-disable-failed", log.Fields{"device-id": device.Id}, err)
+ }
+ }
+ }
+ L2oamDisableOlt(ctx, dh)
+ logger.Debugw(ctx, "olt-disabled", log.Fields{"device-id": 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{}
+
+ dh.stopCollector <- true
+
+ go dh.notifyChildDevices(ctx, "unreachable")
+ cloned := proto.Clone(device).(*voltha.Device)
+ dh.device = cloned
+ if err := dh.coreProxy.PortsStateUpdate(log.WithSpanFromContext(context.TODO(), ctx), cloned.Id, ^uint32(1<<voltha.Port_PON_OLT), voltha.OperStatus_UNKNOWN); err != nil {
+ return olterrors.NewErrAdapter("ports-state-update-failed", log.Fields{"device-id": device.Id}, err)
+ }
+ logger.Debugw(ctx, "disable-device-end", log.Fields{"device-id": device.Id})
+ return nil
+}
+
+func (dh *DeviceHandler) notifyChildDevices(ctx context.Context, state string) {
+ onuInd := oop.OnuIndication{}
+ onuInd.OperState = state
+ onuDevices, err := dh.coreProxy.GetChildDevices(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id)
+ if err != nil {
+ logger.Errorw(ctx, "failed-to-get-child-devices-information", log.Fields{"device-id": dh.device.Id, "error": err})
+ }
+ if onuDevices != nil {
+ for _, onuDevice := range onuDevices.Items {
+ err := dh.AdapterProxy.SendInterAdapterMessage(log.WithSpanFromContext(context.TODO(), ctx), &onuInd, ic.InterAdapterMessageType_ONU_IND_REQUEST,
+ "openolt", onuDevice.Type, onuDevice.Id, onuDevice.ProxyAddress.DeviceId, "")
+ if err != nil {
+ logger.Errorw(ctx, "failed-to-send-inter-adapter-message", log.Fields{"OnuInd": onuInd,
+ "From Adapter": "openolt", "DeviceType": onuDevice.Type, "device-id": 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(ctx context.Context, device *voltha.Device) error {
+ if _, err := dh.Client.ReenableOlt(log.WithSpanFromContext(context.Background(), ctx), new(oop.Empty)); err != nil {
+ if e, ok := status.FromError(err); ok && e.Code() == codes.Internal {
+ return olterrors.NewErrAdapter("olt-reenable-failed", log.Fields{"device-id": dh.device.Id}, err)
+ }
+ }
+ logger.Debug(ctx, "olt-reenabled")
+
+
+ ports, err := dh.coreProxy.ListDevicePorts(ctx, device.Id)
+ if err != nil {
+ return olterrors.NewErrAdapter("list-ports-failed", log.Fields{"device": device.Id}, err)
+ }
+ if err := dh.disableAdminDownPorts(ctx, ports); err != nil {
+ return olterrors.NewErrAdapter("port-status-update-failed-after-olt-reenable", log.Fields{"device": device}, err)
+ }
+ device.OperStatus = voltha.OperStatus_ACTIVE
+ dh.device = device
+
+ if err := dh.coreProxy.DeviceStateUpdate(log.WithSpanFromContext(context.TODO(), ctx), device.Id, device.ConnectStatus, device.OperStatus); err != nil {
+ return olterrors.NewErrAdapter("state-update-failed", log.Fields{
+ "device-id": device.Id,
+ "connect-status": device.ConnectStatus,
+ "oper-status": device.OperStatus}, err)
+ }
+
+ logger.Debugw(ctx, "reenabledevice-end", log.Fields{"device-id": 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))
+ logger.Debugw(ctx, "clearing-resource-data-for-uni-port", log.Fields{"port": port, "uni-id": uniID})
+ /* Delete tech-profile instance from the KV store */
+ if err = dh.flowMgr.DeleteTechProfileInstances(ctx, onu.IntfID, onu.OnuID, uniID, onu.SerialNumber); err != nil {
+ logger.Debugw(ctx, "failed-to-remove-tech-profile-instance-for-onu", log.Fields{"onu-id": onu.OnuID})
+ }
+ logger.Debugw(ctx, "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 {
+ logger.Debugw(ctx, "failed-to-remove-meter-id-for-onu-upstream", log.Fields{"onu-id": onu.OnuID})
+ }
+ logger.Debugw(ctx, "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 {
+ logger.Debugw(ctx, "failed-to-remove-meter-id-for-onu-downstream", log.Fields{"onu-id": onu.OnuID})
+ }
+ logger.Debugw(ctx, "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 {
+ logger.Debugw(ctx, "failed-to-remove-tech-profile-id-for-onu", log.Fields{"onu-id": onu.OnuID})
+ }
+ logger.Debugw(ctx, "removed-tech-profile-id-for-onu", log.Fields{"onu-id": onu.OnuID})
+ if err = dh.resourceMgr.DelGemPortPktInOfAllServices(ctx, onu.IntfID, onu.OnuID, uint32(port)); err != nil {
+ logger.Debugw(ctx, "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 olterrors.NewErrNotFound("resource-manager", log.Fields{"device-id": dh.device.Id}, nil)
+ }
+ nni, err := dh.resourceMgr.GetNNIFromKVStore(ctx)
+ if err != nil {
+ return olterrors.NewErrPersistence("get", "nni", 0, nil, err)
+ }
+ logger.Debugw(ctx, "nni-", log.Fields{"nni": nni})
+ for _, nniIntfID := range nni {
+ flowIDs := dh.resourceMgr.GetCurrentFlowIDsForOnu(ctx, uint32(nniIntfID), int32(nniOnuID), int32(nniUniID))
+ logger.Debugw(ctx, "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 olterrors.NewErrPersistence("clear", "nni", 0, nil, err)
+ }
+ 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 {
+ logger.Debug(ctx, "function-entry-delete-device")
+ /* 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
+ */
+ L2oamDeleteOlt(ctx, dh)
+ go dh.cleanupDeviceResources(ctx)
+ logger.Debug(ctx, "removed-device-from-Resource-manager-KV-store")
+ dh.stopCollector <- true
+ dh.stopHeartbeatCheck <- true
+ if dh.Client != nil {
+ if _, err := dh.Client.Reboot(ctx, new(oop.Empty)); err != nil {
+ return olterrors.NewErrAdapter("olt-reboot-failed", log.Fields{"device-id": dh.device.Id}, 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 olterrors.NewErrAdapter("device-state-update-failed", log.Fields{
+ "device-id": device.Id,
+ "connect-status": cloned.ConnectStatus,
+ "oper-status": cloned.OperStatus}, err).Log()
+ }
+ return nil
+}
+func (dh *DeviceHandler) cleanupDeviceResources(ctx context.Context) {
+
+ 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 {
+ _ = olterrors.NewErrNotFound("onu", log.Fields{
+ "device-id": dh.device.Id,
+ "pon-port": ponPort}, err).Log()
+ }
+ for _, onu := range onuGemData {
+ onuID := make([]uint32, 1)
+ logger.Debugw(ctx, "onu-data", log.Fields{"onu": onu})
+ if err = dh.clearUNIData(ctx, &onu); err != nil {
+ logger.Errorw(ctx, "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 {
+ logger.Errorw(ctx, "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 {
+ logger.Errorw(ctx, "failed-to-clear-data-for-NNI-port", log.Fields{"device-id": dh.device.Id})
+ }
+
+ /* Clear the resource pool for each PON port in the background */
+ go func() {
+ if err := dh.resourceMgr.Delete(ctx); err != nil {
+ logger.Debug(ctx, err)
+ }
+ }()
+ }
+
+ /*Delete ONU map for the device*/
+ dh.onus.Range(func(key interface{}, value interface{}) bool {
+ dh.onus.Delete(key)
+ return true
+ })
+
+ /*Delete discovered ONU map for the device*/
+ dh.discOnus.Range(func(key interface{}, value interface{}) bool {
+ dh.discOnus.Delete(key)
+ return true
+ })
+}
+
+//RebootDevice reboots the given device
+func (dh *DeviceHandler) RebootDevice(ctx context.Context, device *voltha.Device) error {
+ L2oamRebootDevice(ctx, dh, device)
+ logger.Debugw(ctx, "rebooted-device-successfully", log.Fields{"device-id": device.Id})
+ return nil
+}
+
+func (dh *DeviceHandler) handlePacketIndication(ctx context.Context, packetIn *oop.PacketIndication) error {
+ if logger.V(log.DebugLevel) {
+ logger.Debugw(ctx, "received-packet-in", log.Fields{
+ "packet-indication": *packetIn,
+ "device-id": dh.device.Id,
+ "packet": hex.EncodeToString(packetIn.Pkt),
+ })
+ }
+ logicalPortNum, err := dh.flowMgr.GetLogicalPortFromPacketIn(ctx, packetIn)
+ if err != nil {
+ return olterrors.NewErrNotFound("logical-port", log.Fields{"packet": hex.EncodeToString(packetIn.Pkt)}, err)
+ }
+ if logger.V(log.DebugLevel) {
+ logger.Debugw(ctx, "sending-packet-in-to-core", log.Fields{
+ "logical-port-num": logicalPortNum,
+ "device-id": dh.device.Id,
+ "packet": hex.EncodeToString(packetIn.Pkt),
+ })
+ }
+
+ if err := dh.coreProxy.SendPacketIn(log.WithSpanFromContext(context.TODO(), ctx), dh.device.Id, logicalPortNum, packetIn.Pkt); err != nil {
+ return olterrors.NewErrCommunication("send-packet-in", log.Fields{
+ "destination": "core",
+ "source": dh.device.Type,
+ "device-id": dh.device.Id,
+ "packet": hex.EncodeToString(packetIn.Pkt),
+ }, err)
+ }
+
+ if logger.V(log.DebugLevel) {
+ logger.Debugw(ctx, "success-sending-packet-in-to-core!", log.Fields{
+ "packet": hex.EncodeToString(packetIn.Pkt),
+ "device-id": dh.device.Id,
+ })
+ }
+ 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 {
+ if logger.V(log.DebugLevel) {
+ logger.Debugw(ctx, "incoming-packet-out", log.Fields{
+ "device-id": dh.device.Id,
+ "egress-port-no": egressPortNo,
+ "pkt-length": len(packet.Data),
+ "packet": hex.EncodeToString(packet.Data),
+ })
+ }
+
+ if disableBBSim {
+ if vlanEnable {
+ byteString := string(packet.Data)
+ searchByteString := string([]byte{0x88, 0x8e, 0x03, 0x00, 0x00, 0x04, 0x03})
+ //if strings.Index(byteString, searchByteString) != -1 {
+ if strings.Contains(byteString, searchByteString) {
+ device := FindL2oamDevice(hex.EncodeToString(packet.Data[:6]))
+ if onu, ok := device.(*L2oamOnuDevice); ok {
+ onu.Base.EapFlag = true
+ }
+ }
+
+ // add s-vlan tag
+ sendPacket := make([]byte, len(packet.Data)+4)
+ copy(sendPacket, packet.Data[:12])
+ copy(sendPacket[12:], svlanTagAuth)
+ copy(sendPacket[16:], packet.Data[12:])
+ GetL2oamHandle().send(sendPacket)
+ } else {
+ GetL2oamHandle().send(packet.Data)
+ }
+ return nil
+ }
+
+ 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.
+ logger.Debugw(ctx, "dropping-lldp-packet-out-on-uni", log.Fields{
+ "device-id": dh.device.Id,
+ })
+ 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:]...)
+ if logger.V(log.DebugLevel) {
+ logger.Debugw(ctx, "packet-now-single-tagged", log.Fields{
+ "packet-data": hex.EncodeToString(packet.Data),
+ "device-id": dh.device.Id,
+ })
+ }
+ }
+ }
+ intfID := IntfIDFromUniPortNum(uint32(egressPortNo))
+ onuID := OnuIDFromPortNum(uint32(egressPortNo))
+ uniID := UniIDFromPortNum(uint32(egressPortNo))
+
+ gemPortID, err := dh.flowMgr.GetPacketOutGemPortID(ctx, intfID, onuID, uint32(egressPortNo), packet.Data)
+ 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.
+ logger.Errorw(ctx, "failed-to-retrieve-gemport-id-for-packet-out", log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "packet": hex.EncodeToString(packet.Data),
+ "device-id": dh.device.Id,
+ })
+ }
+
+ onuPkt := oop.OnuPacket{IntfId: intfID, OnuId: onuID, PortNo: uint32(egressPortNo), GemportId: gemPortID, Pkt: packet.Data}
+
+ if logger.V(log.DebugLevel) {
+ logger.Debugw(ctx, "sending-packet-to-onu", log.Fields{
+ "egress-port-no": egressPortNo,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "gem-port-id": gemPortID,
+ "packet": hex.EncodeToString(packet.Data),
+ "device-id": dh.device.Id,
+ })
+ }
+
+ if _, err := dh.Client.OnuPacketOut(ctx, &onuPkt); err != nil {
+ return olterrors.NewErrCommunication("packet-out-send", log.Fields{
+ "source": "adapter",
+ "destination": "onu",
+ "egress-port-number": egressPortNo,
+ "intf-id": intfID,
+ "oni-id": onuID,
+ "uni-id": uniID,
+ "gem-port-id": gemPortID,
+ "packet": hex.EncodeToString(packet.Data),
+ "device-id": dh.device.Id,
+ }, err)
+ }
+ } else if egressPortType == voltha.Port_ETHERNET_NNI {
+ nniIntfID, err := IntfIDFromNniPortNum(ctx, uint32(egressPortNo))
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{
+ "egress-nni-port": egressPortNo,
+ "device-id": dh.device.Id,
+ }, err)
+ }
+ uplinkPkt := oop.UplinkPacket{IntfId: nniIntfID, Pkt: packet.Data}
+
+ if logger.V(log.DebugLevel) {
+ logger.Debugw(ctx, "sending-packet-to-nni", log.Fields{
+ "uplink-pkt": uplinkPkt,
+ "packet": hex.EncodeToString(packet.Data),
+ "device-id": dh.device.Id,
+ })
+ }
+
+ if _, err := dh.Client.UplinkPacketOut(ctx, &uplinkPkt); err != nil {
+ return olterrors.NewErrCommunication("packet-out-to-nni", log.Fields{
+ "packet": hex.EncodeToString(packet.Data),
+ "device-id": dh.device.Id,
+ }, err)
+ }
+ } else {
+ logger.Warnw(ctx, "packet-out-to-this-interface-type-not-implemented", log.Fields{
+ "egress-port-no": egressPortNo,
+ "egressPortType": egressPortType,
+ "packet": hex.EncodeToString(packet.Data),
+ "device-id": dh.device.Id,
+ })
+ }
+ return nil
+}
+
+func (dh *DeviceHandler) formOnuKey(intfID, onuID uint32) string {
+ return "" + strconv.Itoa(int(intfID)) + "." + strconv.Itoa(int(onuID))
+}
+
+func startHeartbeatCheck(ctx context.Context, dh *DeviceHandler) {
+
+ var timerCheck *time.Timer
+
+ for {
+ heartbeatTimer := time.NewTimer(dh.openOLT.HeartbeatCheckInterval)
+ select {
+ case <-heartbeatTimer.C:
+ ctxWithTimeout, cancel := context.WithTimeout(log.WithSpanFromContext(context.Background(), ctx), dh.openOLT.GrpcTimeoutInterval)
+ if heartBeat, err := dh.Client.HeartbeatCheck(ctxWithTimeout, new(oop.Empty)); err != nil {
+ logger.Warnw(ctx, "hearbeat-failed", log.Fields{"device-id": dh.device.Id})
+ if timerCheck == nil {
+ // start a after func, when expired will update the state to the core
+ timerCheck = time.AfterFunc(dh.openOLT.HeartbeatFailReportInterval, func() { dh.updateStateUnreachable(ctx) })
+ }
+ } else {
+ if timerCheck != nil {
+ if timerCheck.Stop() {
+ logger.Debugw(ctx, "got-hearbeat-within-timeout", log.Fields{"device-id": dh.device.Id})
+ }
+ timerCheck = nil
+ }
+ logger.Debugw(ctx, "hearbeat",
+ log.Fields{"signature": heartBeat,
+ "device-id": dh.device.Id})
+ }
+ cancel()
+ case <-dh.stopHeartbeatCheck:
+ logger.Debugw(ctx, "stopping-heart-beat-check", log.Fields{"device-id": dh.device.Id})
+ return
+ }
+ }
+}
+
+func (dh *DeviceHandler) updateStateUnreachable(ctx context.Context) {
+ device, err := dh.coreProxy.GetDevice(ctx, dh.device.Id, dh.device.Id)
+ if err != nil || device == nil {
+ _ = olterrors.NewErrNotFound("device", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+
+ if device.ConnectStatus == voltha.ConnectStatus_REACHABLE {
+ if err = dh.coreProxy.DeviceStateUpdate(ctx, dh.device.Id, voltha.ConnectStatus_UNREACHABLE, voltha.OperStatus_UNKNOWN); err != nil {
+ _ = olterrors.NewErrAdapter("device-state-update-failed", log.Fields{"device-id": dh.device.Id}, err).LogAt(log.ErrorLevel)
+ }
+ if err = dh.coreProxy.PortsStateUpdate(ctx, dh.device.Id, 0, voltha.OperStatus_UNKNOWN); err != nil {
+ _ = olterrors.NewErrAdapter("port-update-failed", log.Fields{"device-id": dh.device.Id}, err).Log()
+ }
+ go dh.cleanupDeviceResources(ctx)
+
+ dh.lockDevice.RLock()
+ // Stop the read indication only if it the routine is active
+ // The read indication would have already stopped due to failure on the gRPC stream following OLT going unreachable
+ // Sending message on the 'stopIndication' channel again will cause the readIndication routine to immediately stop
+ // on next execution of the readIndication routine.
+ if dh.isReadIndicationRoutineActive {
+ dh.stopIndications <- true
+ }
+ dh.lockDevice.RUnlock()
+
+ dh.transitionMap.Handle(ctx, DeviceInit)
+
+ }
+}
+
+// EnablePort to enable Pon interface
+func (dh *DeviceHandler) EnablePort(ctx context.Context, port *voltha.Port) error {
+ logger.Debugw(ctx, "enable-port", log.Fields{"Device": dh.device, "port": port})
+ return dh.modifyPhyPort(ctx, port, true)
+}
+
+// DisablePort to disable pon interface
+func (dh *DeviceHandler) DisablePort(ctx context.Context, port *voltha.Port) error {
+ logger.Debugw(ctx, "disable-port", log.Fields{"Device": dh.device, "port": port})
+ return dh.modifyPhyPort(ctx, 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(ctx context.Context, port *voltha.Port, enablePort bool) error {
+ logger.Infow(ctx, "modifyPhyPort", log.Fields{"port": port, "Enable": enablePort, "device-id": dh.device.Id})
+ if port.GetType() == voltha.Port_ETHERNET_NNI {
+ // Bug is opened for VOL-2505 to support NNI disable feature.
+ logger.Infow(ctx, "voltha-supports-single-nni-hence-disable-of-nni-not-allowed",
+ log.Fields{"device": dh.device, "port": port})
+ return olterrors.NewErrAdapter("illegal-port-request", log.Fields{
+ "port-type": port.GetType,
+ "enable-state": enablePort}, nil)
+ }
+ 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 olterrors.NewErrAdapter("pon-port-enable-failed", log.Fields{
+ "device-id": dh.device.Id,
+ "port": port}, err)
+ }
+ // updating interface local cache for collecting stats
+ dh.activePorts.Store(ponID, true)
+ logger.Infow(ctx, "enabled-pon-port", log.Fields{"out": out, "device-id": dh.device, "Port": port})
+ } else {
+ operStatus = voltha.OperStatus_UNKNOWN
+ out, err := dh.Client.DisablePonIf(ctx, ponIntf)
+ if err != nil {
+ return olterrors.NewErrAdapter("pon-port-disable-failed", log.Fields{
+ "device-id": dh.device.Id,
+ "port": port}, err)
+ }
+ // updating interface local cache for collecting stats
+ dh.activePorts.Store(ponID, false)
+ logger.Infow(ctx, "disabled-pon-port", log.Fields{"out": out, "device-id": dh.device, "Port": port})
+ }
+ if err := dh.coreProxy.PortStateUpdate(ctx, dh.device.Id, voltha.Port_PON_OLT, port.PortNo, operStatus); err != nil {
+ return olterrors.NewErrAdapter("port-state-update-failed", log.Fields{
+ "device-id": dh.device.Id,
+ "port": port.PortNo}, err)
+ }
+ return nil
+}
+
+//disableAdminDownPorts disables the ports, if the corresponding port Adminstate is disabled on reboot and Renable device.
+func (dh *DeviceHandler) disableAdminDownPorts(ctx context.Context, ports []*voltha.Port) error {
+ for _, port := range ports {
+ if port.AdminState == common.AdminState_DISABLED {
+ if err := dh.DisablePort(ctx, port); err != nil {
+ return olterrors.NewErrAdapter("port-disable-failed", log.Fields{
+ "device-id": dh.device.Id,
+ "port": port}, err)
+ }
+ }
+ }
+ return nil
+}
+
+//populateActivePorts to populate activePorts map
+func (dh *DeviceHandler) populateActivePorts(ctx context.Context, ports []*voltha.Port) {
+ logger.Infow(ctx, "populateActivePorts", log.Fields{"device-id": dh.device.Id})
+ for _, port := range 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 {
+ logger.Debugw(ctx, "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 olterrors.NewErrAdapter("failed-to-load-onu-details",
+ log.Fields{
+ "device-id": dh.device.Id,
+ "onu-id": onuID,
+ "intf-id": intfID}, nil).Log()
+ }
+ var sn *oop.SerialNumber
+ var err error
+ if sn, err = dh.deStringifySerialNumber(onuDevice.(*OnuDevice).serialNumber); err != nil {
+ return olterrors.NewErrAdapter("failed-to-destringify-serial-number",
+ log.Fields{
+ "devicer-id": dh.device.Id,
+ "serial-number": onuDevice.(*OnuDevice).serialNumber}, err).Log()
+ }
+
+ for uniID := 0; uniID < MaxUnisPerOnu; uniID++ {
+ var flowRemoveData pendingFlowRemoveData
+ key := pendingFlowRemoveDataKey{intfID: intfID, onuID: onuID, uniID: uint32(uniID)}
+ dh.lockDevice.RLock()
+ if flowRemoveData, ok = dh.pendingFlowRemoveDataPerSubscriber[key]; !ok {
+ dh.lockDevice.RUnlock()
+ continue
+ }
+ dh.lockDevice.RUnlock()
+
+ logger.Debugw(ctx, "wait-for-flow-remove-complete-before-processing-child-device-lost",
+ log.Fields{"int-id": intfID, "onu-id": onuID, "uni-id": uniID})
+ // Wait for all flow removes to finish first
+ <-flowRemoveData.allFlowsRemoved
+ logger.Debugw(ctx, "flow-removes-complete-for-subscriber",
+ log.Fields{"int-id": intfID, "onu-id": onuID, "uni-id": uniID})
+ }
+
+ onu := &oop.Onu{IntfId: intfID, OnuId: onuID, SerialNumber: sn}
+ if _, err := dh.Client.DeleteOnu(log.WithSpanFromContext(context.Background(), ctx), onu); err != nil {
+ return olterrors.NewErrAdapter("failed-to-delete-onu", log.Fields{
+ "device-id": dh.device.Id,
+ "onu-id": onuID}, err).Log()
+ }
+ var onuGemData []rsrcMgr.OnuGemInfo
+ if onuMgr, ok := dh.resourceMgr.ResourceMgrs[intfID]; !ok {
+ logger.Warnw(ctx, "failed-to-get-resource-manager-for-interface-Id", log.Fields{
+ "device-id": dh.device.Id,
+ "intf-id": intfID})
+ } else {
+ if err := onuMgr.GetOnuGemInfo(ctx, intfID, &onuGemData); err != nil {
+ logger.Warnw(ctx, "failed-to-get-onu-info-for-pon-port", log.Fields{
+ "device-id": dh.device.Id,
+ "intf-id": intfID,
+ "error": err})
+ } else {
+ for i, onu := range onuGemData {
+ if onu.OnuID == onuID && onu.SerialNumber == onuDevice.(*OnuDevice).serialNumber {
+ logger.Debugw(ctx, "onu-data", log.Fields{"onu": onu})
+ if err := dh.clearUNIData(ctx, &onu); err != nil {
+ logger.Warnw(ctx, "failed-to-clear-uni-data-for-onu", log.Fields{
+ "device-id": dh.device.Id,
+ "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:]...)
+ err := onuMgr.AddOnuGemInfo(ctx, intfID, onuGemData)
+ if err != nil {
+ logger.Warnw(ctx, "persistence-update-onu-gem-info-failed", log.Fields{
+ "intf-id": intfID,
+ "onu-device": onu,
+ "onu-gem": onuGemData,
+ "error": err})
+ //Not returning error on cleanup.
+ }
+ logger.Debugw(ctx, "removed-onu-gem-info", log.Fields{"intf": intfID, "onu-device": onu, "onugem": onuGemData})
+ dh.resourceMgr.FreeonuID(ctx, intfID, []uint32{onu.OnuID})
+ break
+ }
+ }
+ }
+ }
+ dh.onus.Delete(onuKey)
+ dh.discOnus.Delete(onuDevice.(*OnuDevice).serialNumber)
+ return nil
+}
+
+func getInPortFromFlow(flow *of.OfpFlowStats) uint32 {
+ for _, field := range flows.GetOfbFields(flow) {
+ if field.Type == flows.IN_PORT {
+ return field.GetPort()
+ }
+ }
+ return InvalidPort
+}
+
+func getOutPortFromFlow(flow *of.OfpFlowStats) uint32 {
+ for _, action := range flows.GetActions(flow) {
+ if action.Type == flows.OUTPUT {
+ if out := action.GetOutput(); out != nil {
+ return out.GetPort()
+ }
+ }
+ }
+ return InvalidPort
+}
+
+func (dh *DeviceHandler) incrementActiveFlowRemoveCount(ctx context.Context, flow *of.OfpFlowStats) {
+ inPort, outPort := getPorts(flow)
+ logger.Debugw(ctx, "increment-flow-remove-count-for-inPort-out-port", log.Fields{"inPort": inPort, "out-port": outPort})
+ if inPort != InvalidPort && outPort != InvalidPort {
+ _, intfID, onuID, uniID := ExtractAccessFromFlow(inPort, outPort)
+ key := pendingFlowRemoveDataKey{intfID: intfID, onuID: onuID, uniID: uniID}
+ logger.Debugw(ctx, "increment-flow-remove-count-for-subscriber", log.Fields{"intf-id": intfID, "onu-id": onuID, "uni-id": uniID})
+
+ dh.lockDevice.Lock()
+ defer dh.lockDevice.Unlock()
+ flowRemoveData, ok := dh.pendingFlowRemoveDataPerSubscriber[key]
+ if !ok {
+ flowRemoveData = pendingFlowRemoveData{
+ pendingFlowRemoveCount: 0,
+ allFlowsRemoved: make(chan struct{}),
+ }
+ }
+ flowRemoveData.pendingFlowRemoveCount++
+ dh.pendingFlowRemoveDataPerSubscriber[key] = flowRemoveData
+
+ logger.Debugw(ctx, "current-flow-remove-count窶妬ncrement",
+ log.Fields{"intf-id": intfID, "onu-id": onuID, "uni-id": uniID,
+ "currCnt": dh.pendingFlowRemoveDataPerSubscriber[key].pendingFlowRemoveCount})
+ }
+}
+
+func (dh *DeviceHandler) decrementActiveFlowRemoveCount(ctx context.Context, flow *of.OfpFlowStats) {
+ inPort, outPort := getPorts(flow)
+ logger.Debugw(ctx, "decrement-flow-remove-count-for-inPort-out-port", log.Fields{"inPort": inPort, "out-port": outPort})
+ if inPort != InvalidPort && outPort != InvalidPort {
+ _, intfID, onuID, uniID := ExtractAccessFromFlow(uint32(inPort), uint32(outPort))
+ key := pendingFlowRemoveDataKey{intfID: intfID, onuID: onuID, uniID: uniID}
+ logger.Debugw(ctx, "decrement-flow-remove-count-for-subscriber", log.Fields{"intf-id": intfID, "onu-id": onuID, "uni-id": uniID})
+
+ dh.lockDevice.Lock()
+ defer dh.lockDevice.Unlock()
+ if val, ok := dh.pendingFlowRemoveDataPerSubscriber[key]; !ok {
+ logger.Fatalf(ctx, "flow-remove-key-not-found", log.Fields{"intf-id": intfID, "onu-id": onuID, "uni-id": uniID})
+ } else {
+ if val.pendingFlowRemoveCount > 0 {
+ val.pendingFlowRemoveCount--
+ }
+ logger.Debugw(ctx, "current-flow-remove-count-after-decrement",
+ log.Fields{"intf-id": intfID, "onu-id": onuID, "uni-id": uniID,
+ "currCnt": dh.pendingFlowRemoveDataPerSubscriber[key].pendingFlowRemoveCount})
+ // If all flow removes have finished, then close the channel to signal the receiver
+ // to go ahead with flow adds.
+ if val.pendingFlowRemoveCount == 0 {
+ close(val.allFlowsRemoved)
+ delete(dh.pendingFlowRemoveDataPerSubscriber, key)
+ return
+ }
+ dh.pendingFlowRemoveDataPerSubscriber[key] = val
+ }
+ }
+}
+
+func (dh *DeviceHandler) waitForFlowRemoveToFinish(ctx context.Context, flow *of.OfpFlowStats) {
+ var flowRemoveData pendingFlowRemoveData
+ var ok bool
+ inPort, outPort := getPorts(flow)
+ logger.Debugw(ctx, "wait-for-flow-remove-to-finish-for-inPort-out-port", log.Fields{"inPort": inPort, "out-port": outPort})
+ if inPort != InvalidPort && outPort != InvalidPort {
+ _, intfID, onuID, uniID := ExtractAccessFromFlow(inPort, outPort)
+ key := pendingFlowRemoveDataKey{intfID: intfID, onuID: onuID, uniID: uniID}
+ logger.Debugw(ctx, "wait-for-flow-remove-to-finish-for-subscriber", log.Fields{"intf-id": intfID, "onu-id": onuID, "uni-id": uniID})
+
+ dh.lockDevice.RLock()
+ if flowRemoveData, ok = dh.pendingFlowRemoveDataPerSubscriber[key]; !ok {
+ logger.Debugw(ctx, "no-pending-flow-to-remove", log.Fields{"intf-id": intfID, "onu-id": onuID, "uni-id": uniID})
+ dh.lockDevice.RUnlock()
+ return
+ }
+ dh.lockDevice.RUnlock()
+
+ // Wait for all flow removes to finish first
+ <-flowRemoveData.allFlowsRemoved
+
+ logger.Debugw(ctx, "all-flows-cleared--handling-flow-add-now", log.Fields{"intf-id": intfID, "onu-id": onuID, "uni-id": uniID})
+ }
+}
+
+func getPorts(flow *of.OfpFlowStats) (uint32, uint32) {
+ inPort := getInPortFromFlow(flow)
+ outPort := getOutPortFromFlow(flow)
+
+ if inPort == InvalidPort || outPort == InvalidPort {
+ return inPort, outPort
+ }
+
+ if isControllerFlow := IsControllerBoundFlow(outPort); isControllerFlow {
+ /* Get UNI port/ IN Port from tunnel ID field for upstream controller bound flows */
+ if portType := IntfIDToPortTypeName(inPort); portType == voltha.Port_PON_OLT {
+ if uniPort := flows.GetChildPortFromTunnelId(flow); uniPort != 0 {
+ return uniPort, outPort
+ }
+ }
+ } else {
+ // Downstream flow from NNI to PON port , Use tunnel ID as new OUT port / UNI port
+ if portType := IntfIDToPortTypeName(outPort); portType == voltha.Port_PON_OLT {
+ if uniPort := flows.GetChildPortFromTunnelId(flow); uniPort != 0 {
+ return inPort, uniPort
+ }
+ // Upstream flow from PON to NNI port , Use tunnel ID as new IN port / UNI port
+ } else if portType := IntfIDToPortTypeName(inPort); portType == voltha.Port_PON_OLT {
+ if uniPort := flows.GetChildPortFromTunnelId(flow); uniPort != 0 {
+ return uniPort, outPort
+ }
+ }
+ }
+
+ return InvalidPort, InvalidPort
+}
+
+func extractOmciTransactionID(omciPkt []byte) uint16 {
+ if len(omciPkt) > 3 {
+ d := omciPkt[0:2]
+ transid := binary.BigEndian.Uint16(d)
+ return transid
+ }
+ return 0
+}
+
+// StoreOnuDevice stores the onu parameters to the local cache.
+func (dh *DeviceHandler) StoreOnuDevice(onuDevice *OnuDevice) {
+ onuKey := dh.formOnuKey(onuDevice.intfID, onuDevice.onuID)
+ dh.onus.Store(onuKey, onuDevice)
+}
+
+func (dh *DeviceHandler) getExtValue(ctx context.Context, device *voltha.Device, value voltha.ValueType_Type) (*voltha.ReturnValues, error) {
+ var err error
+ var sn *oop.SerialNumber
+ var ID uint32
+ resp := new(voltha.ReturnValues)
+ valueparam := new(oop.ValueParam)
+ ctx = log.WithSpanFromContext(context.Background(), ctx)
+ logger.Infow(ctx, "getExtValue", log.Fields{"onu-id": device.Id, "pon-intf": device.ParentPortNo})
+ if sn, err = dh.deStringifySerialNumber(device.SerialNumber); err != nil {
+ return nil, err
+ }
+ ID = device.ProxyAddress.GetOnuId()
+ Onu := oop.Onu{IntfId: device.ParentPortNo, OnuId: ID, SerialNumber: sn}
+ valueparam.Onu = &Onu
+ valueparam.Value = value
+
+ resp.Unsupported = uint32(value)
+ _ = ctx
+
+ /*
+ resp, err = dh.Client.GetValue(ctx, valueparam)
+ if err != nil {
+ logger.Errorw("error-while-getValue", log.Fields{"DeviceID": dh.device, "onu-id": onuid, "error": err})
+ return nil, err
+ }
+ */
+
+ logger.Infow(ctx, "get-ext-value", log.Fields{"resp": resp, "device-id": dh.device, "onu-id": device.Id, "pon-intf": device.ParentPortNo})
+ return resp, nil
+}
+
+// func (dh *DeviceHandler) L2oamCmdRequest2(ctx context.Context, device *voltha.Device, request *voltha.SimulateAlarmRequest) error {
+// logger.Infow(ctx, "L2oamRequest", log.Fields{"device-id": device.Id})
+
+// switch request.Drift {
+// case 0: // add-flow
+// L2oamAfterKeepAlive(ctx, dh)
+// L2oamAddFlow(ctx, dh)
+
+// case 1: // add-flow-dev
+// tomiObjContext := L2oamAddFlowToDeviceDS(ctx, dh)
+// if tomiObjContext != nil {
+// L2oamAddFlowToDeviceUS(ctx, dh, tomiObjContext)
+// }
+// }
+
+// return nil
+// }
+
+// L2oamCmdRequest executes some commands for L2OAM
+func (dh *DeviceHandler) L2oamCmdRequest(ctx context.Context, device *voltha.Device, request *voltha.SimulateAlarmRequest) error {
+ logger.Infow(ctx, "L2oamCmdRequest()", log.Fields{"device-id": device.Id})
+
+ cmd := parseL2oamCmd(request)
+ logger.Info(ctx, "parseL2oamCmd() request=%v, cmd=%v", request, cmd)
+
+ switch cmd.Type {
+ case "add-flow":
+ //L2oamAfterKeepAlive(ctx, dh)
+ L2oamAddFlow(ctx, dh, cmd)
+
+ case "add-flow-dev":
+ err := L2oamAddFlowToDeviceAll(ctx, dh, cmd)
+ if err == nil {
+ onu := FindL2oamDeviceByDeviceID(cmd.OnuDeviceID)
+ if onu == nil {
+ logger.Debug(ctx, fmt.Sprintf("L2oamCmdRequest() FindL2oamDevice() onu not found. deviceId=%s", cmd.OnuDeviceID))
+ } else {
+ // start ONU mount sequence
+ onu.startMountSequence(context.Background(), l2oam.OnuPkgType, cmd)
+ }
+ }
+ default:
+ logger.Error(ctx, "L2oamCmdRequest() cmd.Type error. request=%v, cmd=%v", request, cmd)
+ }
+
+ return nil
+}
+
+// L2oamCmd contains command arguments for L2OAM command
+type L2oamCmd struct {
+ Type string
+ Cir []byte
+ Pir []byte
+ Tpid []byte
+ Vid []byte
+ Itpid []byte
+ Ivid []byte
+ OnuDeviceID string
+}
+
+func parseL2oamCmd(request *voltha.SimulateAlarmRequest) *L2oamCmd {
+ l2oamCmd := &L2oamCmd{
+ Type: "unknown",
+ Cir: []byte{0x00, 0x00, 0x03, 0xe8},
+ Pir: []byte{0x00, 0x98, 0x96, 0x80},
+ Tpid: []byte{0x88, 0xa8},
+ Vid: []byte{0x00, 0x64},
+ Itpid: []byte{0x81, 0x00},
+ Ivid: []byte{0x00, 0x0a},
+ OnuDeviceID: "",
+ }
+
+ if request.Indicator == "" {
+ return l2oamCmd
+ }
+ cmdStr := strings.Replace(request.Indicator, "\"", "", -1)
+ if request.Drift == 0 {
+ l2oamCmd.Type = "add-flow"
+ } else {
+ l2oamCmd.Type = "add-flow-dev"
+ }
+
+ cmds := strings.Split(cmdStr, ",")
+ for _, cmd := range cmds {
+ kv := strings.Split(cmd, "=")
+ key := kv[0]
+ value := kv[1]
+ switch key {
+ case "cir":
+ bytes, _ := hex.DecodeString(value)
+ l2oamCmd.Cir = bytes
+ case "pir":
+ bytes, _ := hex.DecodeString(value)
+ l2oamCmd.Pir = bytes
+ case "tpid":
+ bytes, _ := hex.DecodeString(value)
+ l2oamCmd.Tpid = bytes
+ case "vid":
+ bytes, _ := hex.DecodeString(value)
+ l2oamCmd.Vid = bytes
+ case "itpid":
+ bytes, _ := hex.DecodeString(value)
+ l2oamCmd.Itpid = bytes
+ case "ivid":
+ bytes, _ := hex.DecodeString(value)
+ l2oamCmd.Ivid = bytes
+ case "onudeviceid":
+ l2oamCmd.OnuDeviceID = value
+ default:
+ }
+ }
+ return l2oamCmd
+}
diff --git a/internal/pkg/core/device_handler_test.go b/internal/pkg/core/device_handler_test.go
new file mode 100644
index 0000000..70b92bb
--- /dev/null
+++ b/internal/pkg/core/device_handler_test.go
@@ -0,0 +1,1184 @@
+/*
+ * 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 (
+ "context"
+ "net"
+ "reflect"
+ "sync"
+ "testing"
+ "time"
+
+ "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"
+ "github.com/opencord/voltha-lib-go/v3/pkg/pmmetrics"
+ ponrmgr "github.com/opencord/voltha-lib-go/v3/pkg/ponresourcemanager"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/config"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/olterrors"
+ "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 newMockCoreProxy() *mocks.MockCoreProxy {
+ mcp := mocks.MockCoreProxy{
+ Devices: make(map[string]*voltha.Device),
+ DevicePorts: make(map[string][]*voltha.Port),
+ }
+ var pm []*voltha.PmConfig
+ mcp.Devices["olt"] = &voltha.Device{
+ Id: "olt",
+ Root: true,
+ ParentId: "logical_device",
+ ParentPortNo: 1,
+ 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.DevicePorts["olt"] = []*voltha.Port{
+ {PortNo: 1, Label: "pon"},
+ {PortNo: 2, Label: "nni"},
+ }
+
+ mcp.Devices["onu1"] = &voltha.Device{
+ Id: "1",
+ Root: false,
+ ParentId: "olt",
+ ParentPortNo: 1,
+
+ 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.DevicePorts["onu1"] = []*voltha.Port{
+ {PortNo: 1, Label: "pon"},
+ {PortNo: 2, Label: "uni"},
+ }
+
+ mcp.Devices["onu2"] = &voltha.Device{
+ Id: "2",
+ Root: false,
+ ParentId: "olt",
+ OperStatus: 2,
+
+ 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,
+ },
+ }
+ mcp.DevicePorts["onu2"] = []*voltha.Port{
+ {PortNo: 1, Label: "pon"},
+ {PortNo: 2, Label: "uni"},
+ }
+ return &mcp
+}
+
+func newMockDeviceHandler() *DeviceHandler {
+ device := &voltha.Device{
+ Id: "olt",
+ Root: true,
+ ParentId: "logical_device",
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ DeviceId: "olt",
+ DeviceType: "onu",
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ },
+ ConnectStatus: 1,
+ MacAddress: "08:00:27:1c:18:55",
+ }
+ cp := newMockCoreProxy()
+ ap := &mocks.MockAdapterProxy{}
+ ep := &mocks.MockEventProxy{}
+ cnf := &config.AdapterFlags{
+ InterfaceName: "enp0s3",
+ SrcMac: "08:00:27:1c:18:9a",
+ }
+ openOLT := &OpenOLT{
+ coreProxy: cp,
+ adapterProxy: ap,
+ eventProxy: ep,
+ config: cnf,
+ }
+ dh := NewDeviceHandler(cp, ap, ep, device, openOLT)
+ oopRanges := []*oop.DeviceInfo_DeviceResourceRanges{{
+ IntfIds: []uint32{0, 1},
+ Technology: "xgs-pon",
+ Pools: []*oop.DeviceInfo_DeviceResourceRanges_Pool{{}},
+ }}
+
+ deviceInf := &oop.DeviceInfo{Vendor: "openolt", Ranges: oopRanges, Model: "openolt", DeviceId: dh.device.Id, PonPorts: 2}
+ rsrMgr := resourcemanager.OpenOltResourceMgr{DeviceID: dh.device.Id, DeviceType: dh.device.Type, DevInfo: deviceInf,
+ KVStore: &db.Backend{
+ Client: &mocks.MockKVClient{},
+ }}
+ rsrMgr.AllocIDMgmtLock = make([]sync.RWMutex, deviceInf.PonPorts)
+ rsrMgr.GemPortIDMgmtLock = make([]sync.RWMutex, deviceInf.PonPorts)
+ rsrMgr.OnuIDMgmtLock = make([]sync.RWMutex, deviceInf.PonPorts)
+
+ dh.resourceMgr = &rsrMgr
+ 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{0, 1},
+ KVStore: &db.Backend{
+ Client: &mocks.MockKVClient{},
+ },
+ PonResourceRanges: ranges,
+ SharedIdxByType: sharedIdxByType,
+ }
+ dh.resourceMgr.ResourceMgrs[0] = ponmgr
+ dh.resourceMgr.ResourceMgrs[1] = 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 = ""
+ return dh
+}
+
+func Test_generateMacFromHost(t *testing.T) {
+
+ ctx := context.Background()
+ 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(ctx, 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) {
+ ctx := context.Background()
+ 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(&olterrors.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(ctx, 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(&olterrors.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) {
+ ctx := context.Background()
+ 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 {
+ logger.Errorw(ctx, "cannot-marshal-request", log.Fields{"error": err})
+ }
+
+ var marshalledData1 *any.Any
+
+ if marshalledData1, err = ptypes.MarshalAny(body2); err != nil {
+ logger.Errorw(ctx, "cannot-marshal-request", log.Fields{"error": err})
+ }
+ var marshalledData2 *any.Any
+
+ if marshalledData2, err = ptypes.MarshalAny(body3); err != nil {
+ logger.Errorw(ctx, "cannot-marshal-request", log.Fields{"error": err})
+ }
+ type args struct {
+ msg *ic.InterAdapterMessage
+ }
+ invalid := reflect.TypeOf(&olterrors.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(&olterrors.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,
+ }}, nil},
+ */
+ {"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(ctx, 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) {
+ ctx := context.Background()
+ 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(ctx, tt.args.onuDevice, tt.args.omciMsg)
+ //TODO: actually verify test cases
+ })
+ }
+}
+
+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(context.Background(), tt.args.logicalPort, tt.args.packetPayload)
+ //TODO: actually verify test cases
+ })
+ }
+}
+
+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(context.Background(), 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(context.Background(), 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
+ }{
+ // TODO: Add test cases.
+ {"RebootDevice-1", dh1, args{device: dh1.device}},
+ {"RebootDevice-2", dh1, args{device: dh2.device}},
+ {"RebootDevice-3", dh2, args{device: dh2.device}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+
+ if err := tt.devicehandler.RebootDevice(context.Background(), tt.args.device); err != nil {
+ t.Errorf("DeviceHandler.RebootDevice() error = %v", err)
+ }
+ })
+ }
+}
+
+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", false))
+ dh3.onus.Store("onu2", NewOnuDevice("onu2", "onu2", "onu2", 2, 2, "onu2", false))
+
+ 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(context.Background(), tt.args.intfID, tt.args.portType, tt.args.state)
+ //TODO: actually verify test cases
+ })
+ }
+}
+
+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()
+ if err := dh.handleOltIndication(ctx, tt.args.oltIndication); err != nil {
+ t.Error(err)
+ }
+ })
+ }
+}
+
+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()
+ if err := tt.devicehandler.postInit(ctx); err != nil {
+ t.Error(err)
+ }
+ })
+ }
+}
+
+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: 0, onuID: 1, serialNum: &oop.SerialNumber{VendorId: []byte("onu1")}}},
+ {"activateONU-2", dh, args{intfID: 1, onuID: 2, serialNum: &oop.SerialNumber{VendorId: []byte("onu2")}}},
+ {"activateONU-3", dh1, args{intfID: 0, onuID: 1, serialNum: &oop.SerialNumber{VendorId: []byte("onu1")}}},
+ {"activateONU-4", dh1, args{intfID: 1, 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)
+ //TODO: actually verify test cases
+ })
+ }
+}
+
+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-3", dh2, args{egressPortNo: 4112, packet: pktout}, false},
+ {"PacketOut-4", dh1, args{egressPortNo: 1048577, packet: pktout}, false},
+ {"PacketOut-5", dh2, args{egressPortNo: 1048576, 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.device.Id = ""
+ 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)
+ }
+ tt.devicehandler.stopCollector <- true //stop the stat collector invoked from doStateUp
+ })
+ }
+}
+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)
+ //TODO: should fail this test case (Errorf) if result is not as expected
+ }
+ })
+ }
+}
+
+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_onuDiscIndication(t *testing.T) {
+
+ dh1 := newMockDeviceHandler()
+ dh1.discOnus = sync.Map{}
+ dh1.discOnus.Store("onu1", true)
+ dh1.discOnus.Store("onu2", false)
+ dh1.discOnus.Store("onu3", true)
+ dh1.discOnus.Store("onu4", true)
+ dh1.onus = sync.Map{}
+ dh1.onus.Store("onu3", NewOnuDevice("onu3", "onu3", "onu3", 3, 3, "onu3", true))
+ dh1.onus.Store("onu4", NewOnuDevice("onu4", "onu4", "onu4", 4, 4, "onu4", true))
+ 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", dh1, args{onuDiscInd: &oop.OnuDiscIndication{IntfId: 3, SerialNumber: &oop.SerialNumber{VendorId: []byte("TWSH"), VendorSpecific: []byte("1234")}}, sn: "onu3"}},
+ {"onuDiscIndication-8", dh1, args{onuDiscInd: &oop.OnuDiscIndication{IntfId: 3, SerialNumber: &oop.SerialNumber{VendorId: []byte("TWSH"), VendorSpecific: []byte("1234")}}, sn: "onu4"}},
+ {"onuDiscIndication-9", 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)
+ //TODO: actually verify test cases
+ })
+ }
+}
+
+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, true},
+ {"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(context.Background())
+ 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()
+ 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)
+ // TODO: actually verify test cases
+ })
+ }
+}
+
+func Test_startCollector(t *testing.T) {
+ type args struct {
+ dh *DeviceHandler
+ }
+ dh := newMockDeviceHandler()
+ dh.coreProxy.(*mocks.MockCoreProxy).DevicePorts[dh.device.Id] = []*voltha.Port{
+ {PortNo: 1, Label: "pon", Type: voltha.Port_PON_OLT},
+ {PortNo: 1048577, Label: "nni", Type: voltha.Port_ETHERNET_NNI},
+ {PortNo: 1048578, Label: "nni", Type: voltha.Port_ETHERNET_NNI},
+ }
+ dh.portStats.NorthBoundPort = make(map[uint32]*NniPort)
+ dh.portStats.NorthBoundPort[1] = &NniPort{Name: "OLT-1"}
+ dh.portStats.NorthBoundPort[2] = &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"}
+ }
+ dh1 := newMockDeviceHandler()
+ dh1.coreProxy.(*mocks.MockCoreProxy).DevicePorts[dh.device.Id] = []*voltha.Port{}
+ tests := []struct {
+ name string
+ args args
+ }{
+ // TODO: Add test cases.
+ {"StartCollector-1", args{dh}},
+ {"StartCollector-2", args{dh1}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ go func() {
+ time.Sleep(1 * time.Second) // simulated wait time to stop startCollector
+ tt.args.dh.stopCollector <- true
+ }()
+ startCollector(context.Background(), tt.args.dh)
+ })
+ }
+}
diff --git a/internal/pkg/core/l2oam/common.go b/internal/pkg/core/l2oam/common.go
new file mode 100644
index 0000000..b77cf3d
--- /dev/null
+++ b/internal/pkg/core/l2oam/common.go
@@ -0,0 +1,50 @@
+/*
+ * 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 l2oam Common Logger initialization
+package l2oam
+
+import (
+ "context"
+ "fmt"
+ "time"
+)
+
+//var logger log.CLogger
+var logger l2OAMLogger
+
+// func init() {
+// // Setup this package so that it's log level can be modified at run time
+// var err error
+// logger, err = log.RegisterPackage(log.JSON, log.ErrorLevel, log.Fields{})
+// if err != nil {
+// panic(err)
+// }
+// }
+
+func init() {
+ logger = l2OAMLogger{}
+ logger.Debug(context.Background(), "initialize l2oam logger")
+}
+
+//l2OAMLogger ...
+type l2OAMLogger struct {
+}
+
+//Debug ...
+func (l *l2OAMLogger) Debug(ctx context.Context, message string) {
+ fmt.Printf("%v:%s\n", time.Now(), message)
+}
diff --git a/internal/pkg/core/l2oam/message.go b/internal/pkg/core/l2oam/message.go
new file mode 100644
index 0000000..dd092da
--- /dev/null
+++ b/internal/pkg/core/l2oam/message.go
@@ -0,0 +1,129 @@
+/*
+ * 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 l2oam
+
+import (
+ "net"
+ "sync"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// OnuPkgType describes target package type
+var OnuPkgType = "PkgB"
+
+// OnuPkgTypeA is a constant of package
+const OnuPkgTypeA = "PkgA"
+
+// OnuPkgTypeB is a constant of package
+const OnuPkgTypeB = "PkgB"
+
+// TibitFrame is a typical structure of a frame
+type TibitFrame struct {
+ layers.BaseLayer
+ Data []byte
+}
+
+// LayerType returns ethernet layer type
+func (t *TibitFrame) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (t *TibitFrame) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ length := len(t.Data)
+ bytes, err := b.PrependBytes(length)
+ if err != nil {
+ return err
+ }
+ copy(bytes, t.Data)
+ return nil
+}
+
+// TomiObjectContext is a structure for tomi object
+type TomiObjectContext struct {
+ Branch uint8
+ Type uint16
+ Length uint8
+ Instance uint32
+}
+
+// CreateMessage creates l2 message
+func CreateMessage(srcMac string, dstMac string, ethernetType layers.EthernetType, tibitData gopacket.SerializableLayer) []byte {
+ srcMAC, _ := net.ParseMAC(srcMac)
+ dstMAC, _ := net.ParseMAC(dstMac)
+
+ ethernetLayer := &layers.Ethernet{
+ SrcMAC: srcMAC,
+ DstMAC: dstMAC,
+ EthernetType: ethernetType,
+ }
+
+ buf := gopacket.NewSerializeBuffer()
+ if err := gopacket.SerializeLayers(
+ buf,
+ gopacket.SerializeOptions{
+ ComputeChecksums: true,
+ FixLengths: true,
+ },
+ ethernetLayer,
+ tibitData,
+ ); err != nil {
+ return buf.Bytes()
+ }
+ return buf.Bytes()
+}
+
+// CreateMessage creates vlan message
+func CreateMessageVlan(srcMac string, dstMac string, ethernetType layers.EthernetType, tibitData gopacket.SerializableLayer, vlanLayer *layers.Dot1Q) []byte {
+ srcMAC, _ := net.ParseMAC(srcMac)
+ dstMAC, _ := net.ParseMAC(dstMac)
+
+ ethernetLayer := &layers.Ethernet{
+ SrcMAC: srcMAC,
+ DstMAC: dstMAC,
+ EthernetType: ethernetType,
+ }
+
+ buf := gopacket.NewSerializeBuffer()
+ if err := gopacket.SerializeLayers(
+ buf,
+ gopacket.SerializeOptions{
+ ComputeChecksums: true,
+ FixLengths: true,
+ },
+ ethernetLayer,
+ vlanLayer,
+ tibitData,
+ ); err != nil {
+ return buf.Bytes()
+ }
+ return buf.Bytes()
+}
+
+// CorrelationTag instance ID
+// It is incremented automatically for each TOMI message with OLT
+var instance uint32 = 0x5c1f6a60
+var instanceMutex sync.Mutex
+
+func getOltInstance() uint32 {
+ instanceMutex.Lock()
+ defer instanceMutex.Unlock()
+ instance = instance + 1
+
+ return instance
+
+}
diff --git a/internal/pkg/core/l2oam/msg_after_keep_alive.go b/internal/pkg/core/l2oam/msg_after_keep_alive.go
new file mode 100644
index 0000000..4772e11
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_after_keep_alive.go
@@ -0,0 +1,35 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+
+ "github.com/google/gopacket"
+)
+
+func GeneratePcscompuTibitcom() gopacket.SerializableLayer {
+
+ data := &TibitFrame{
+ Data: []byte{0x03, 0x00, 0x50, 0xfe, 0x2a, 0xea, 0x15, 0x01, 0x0c, 0x0c, 0x7a, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x11, 0x04, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x07, 0x00, 0x00},
+ }
+
+ binary.BigEndian.PutUint32(data.Data[12:16], getOltInstance())
+
+ return data
+
+}
diff --git a/internal/pkg/core/l2oam/msg_autonomous.go b/internal/pkg/core/l2oam/msg_autonomous.go
new file mode 100644
index 0000000..dff723d
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_autonomous.go
@@ -0,0 +1,71 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// AutonomousEvent is a structure for Autonomous Event
+type AutonomousEvent struct {
+ ComResp TOAMGetResponse
+ EcLength uint8
+ EcValue []byte
+ EndBranch uint8
+}
+
+// String returns the string expression of AutonomousEvent
+func (d *AutonomousEvent) String() string {
+ message := d.ComResp.String()
+ message = fmt.Sprintf("%s, EcLength:%v, EcValue:%v, EndBranch:%v", message, d.EcLength, hex.EncodeToString(d.EcValue), d.EndBranch)
+ return message
+}
+
+// Len returns the length of AutonomousEvent
+func (d *AutonomousEvent) Len() int {
+ return d.ComResp.Len() + int(d.ComResp.VcLength) + 1
+}
+
+// LayerType returns the ethernet type of AutonomousEvent
+func (d *AutonomousEvent) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *AutonomousEvent) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ return nil
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *AutonomousEvent) Decode(data []byte) error {
+ d.ComResp.Decode(data)
+ i := d.ComResp.Len()
+ d.EcLength = data[i]
+ i++
+ d.EcValue = data[i : i+int(d.EcLength)]
+ i = i + int(d.EcLength)
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// IsRegistrationStatusMessage returns true if the message is for ONU registration
+func (d *AutonomousEvent) IsRegistrationStatusMessage() bool {
+ return d.ComResp.VcBranch == 0x01 && d.ComResp.VcLeaf == 0x0009
+}
diff --git a/internal/pkg/core/l2oam/msg_best_effort_mbs.go b/internal/pkg/core/l2oam/msg_best_effort_mbs.go
new file mode 100644
index 0000000..fe63edf
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_best_effort_mbs.go
@@ -0,0 +1,56 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateBestEffortMbs generates "Traffic Profile/Best Effort MBS" message
+func GenerateBestEffortMbs() gopacket.SerializableLayer {
+
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x070f,
+ OCLength: 4,
+ OCInstance: 0x00000002,
+ // Vc
+ VcBranch: 0x7f,
+ VcLeaf: 0x0009,
+ VcLength: 5,
+ // EC
+ ECLength: 4,
+ ECValue: []byte{0x00, 0x06, 0x40, 0x00},
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_best_effort_rate.go b/internal/pkg/core/l2oam/msg_best_effort_rate.go
new file mode 100644
index 0000000..42ccd43
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_best_effort_rate.go
@@ -0,0 +1,58 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+
+ "github.com/google/gopacket"
+)
+
+// GenerateBestEffortRate generates "Traffic Profile/Best Effort Rate" message
+func GenerateBestEffortRate(ecvalue []byte, trafficProfile []byte) gopacket.SerializableLayer {
+
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x070f,
+ OCLength: 4,
+ OCInstance: binary.BigEndian.Uint32(trafficProfile),
+ // Vc
+ VcBranch: 0x7f,
+ VcLeaf: 0x0008,
+ VcLength: uint8(1 + len(ecvalue)), //5
+ // EC
+ ECLength: uint8(len(ecvalue)), //4
+ ECValue: ecvalue, //[]byte{0x00, 0x4c, 0x4b, 0x40}
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_best_effort_rate_test.go b/internal/pkg/core/l2oam/msg_best_effort_rate_test.go
new file mode 100644
index 0000000..25bfb49
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_best_effort_rate_test.go
@@ -0,0 +1,81 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "reflect"
+ "testing"
+
+ "github.com/google/gopacket"
+)
+
+func TestGenerateBestEffortRate(t *testing.T) {
+ type args struct {
+ ecvalue []byte
+ trafficProfile []byte
+ }
+ tests := []struct {
+ name string
+ args args
+ want gopacket.SerializableLayer
+ }{
+ // TODO: Add test cases.
+ {
+ name: "GenerateBestEffortRate-1",
+ args: args{
+ ecvalue: []byte{0x00, 0x4c, 0x4b, 0x40},
+ trafficProfile: []byte{0x00, 0x00, 0x00, 0x01},
+ },
+ want: &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance() + 1,
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x070f,
+ OCLength: 4,
+ OCInstance: binary.BigEndian.Uint32([]byte{0x00, 0x00, 0x00, 0x01}),
+ // Vc
+ VcBranch: 0x7f,
+ VcLeaf: 0x0008,
+ VcLength: 5, //5
+ // EC
+ ECLength: 4, //4
+ ECValue: []byte{0x00, 0x4c, 0x4b, 0x40}, //[]byte{0x00, 0x4c, 0x4b, 0x40}
+ // End
+ EndBranch: 0x00,
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := GenerateBestEffortRate(tt.args.ecvalue, tt.args.trafficProfile); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("GenerateBestEffortRate() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/internal/pkg/core/l2oam/msg_discovery.go b/internal/pkg/core/l2oam/msg_discovery.go
new file mode 100644
index 0000000..ab104bc
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_discovery.go
@@ -0,0 +1,396 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// GenerateDiscoverySOLICIT generates "Discovery: SOLICIT" message
+func GenerateDiscoverySOLICIT() gopacket.SerializableLayer {
+
+ tibitData := &DiscoverySolicit{
+ // IEEE 1904.2
+ Opcode: 0xfd,
+ DiscoveryType: 0x01,
+ // Vendor-specific
+ VendorType: 0xfe,
+ Length: 37,
+ // Vendor ID
+ VendorIDType: 0xfd,
+ VendorIDLength: 3,
+ VendorID: []byte{0x2a, 0xea, 0x15},
+ // Controller Priority
+ CPType: 0x05,
+ CPLength: 1,
+ CPValue: []byte{128},
+ //NetworkID
+ NWType: 0x06,
+ NWLength: 16,
+ NWValue: []byte("tibitcom.com"),
+ //Device Type
+ DVType: 0x07,
+ DVLength: 1,
+ DVValue: []byte{1},
+ //Supported CLient Protocols
+ SCPType: 0x08,
+ SCPLength: 1,
+ SCPValue: []byte{0x03},
+ //Padding
+ PadType: 0xff,
+ PadLength: 1,
+ PadValue: []byte{0},
+ }
+
+ return tibitData
+}
+
+// DiscoverySolicit is a structure for Discovery message
+type DiscoverySolicit struct {
+ layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16 // length of after this data without Padding
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ CPType uint8
+ CPLength uint16
+ CPValue []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+}
+
+// String returns the string expression of DiscoverySolicit
+func (d *DiscoverySolicit) String() string {
+ message := fmt.Sprintf("Opcode:%v, DiscoveryType:%v, VendorType:%v, Length:%v", d.Opcode, d.DiscoveryType, d.VendorType, d.Length)
+ message = fmt.Sprintf("%s, VendorIDType:%v, VendorIDLength:%v, VendorID:%v", message, d.VendorIDType, d.VendorIDLength, hex.EncodeToString(d.VendorID))
+ message = fmt.Sprintf("%s, CPType:%v, CPLength:%v, CPValue:%v", message, d.CPType, d.CPLength, hex.EncodeToString(d.CPValue))
+ message = fmt.Sprintf("%s, NWType:%v, NWLength:%v, NWValue:%v", message, d.NWType, d.NWLength, hex.EncodeToString(d.NWValue))
+ message = fmt.Sprintf("%s, DVType:%v, DVLength:%v, DVValue:%v", message, d.DVType, d.DVLength, hex.EncodeToString(d.DVValue))
+ message = fmt.Sprintf("%s, SCPType:%v, SCPLength:%v, SCPValue:%v", message, d.SCPType, d.SCPLength, hex.EncodeToString(d.SCPValue))
+ return message
+}
+
+// Len returns the length of DiscoverySolicit
+func (d *DiscoverySolicit) Len() int {
+ len := (2) + (3) + int(d.Length) + (int(d.PadLength) + 3)
+ return len
+}
+
+// LayerType returns the ethernet type of DiscoverySolicit
+func (d *DiscoverySolicit) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *DiscoverySolicit) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ data[i] = byte(d.DiscoveryType)
+ i++
+ data[i] = byte(d.VendorType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Length)
+ i = i + 2
+
+ data[i] = byte(d.VendorIDType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VendorIDLength)
+ i = i + 2
+ copy(data[i:i+int(d.VendorIDLength)], d.VendorID)
+ i = i + int(d.VendorIDLength)
+
+ data[i] = byte(d.CPType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.CPLength)
+ i = i + 2
+ copy(data[i:i+int(d.CPLength)], d.CPValue)
+ i = i + int(d.CPLength)
+
+ data[i] = byte(d.NWType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.NWLength)
+ i = i + 2
+ copy(data[i:i+int(d.NWLength)], d.NWValue)
+ i = i + int(d.NWLength)
+
+ data[i] = byte(d.DVType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.DVLength)
+ i = i + 2
+ copy(data[i:i+int(d.DVLength)], d.DVValue)
+ i = i + int(d.DVLength)
+
+ data[i] = byte(d.SCPType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.SCPLength)
+ i = i + 2
+ copy(data[i:i+int(d.SCPLength)], d.SCPValue)
+ i = i + int(d.SCPLength)
+
+ data[i] = byte(d.PadType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.PadLength)
+ i = i + 2
+ copy(data[i:i+int(d.PadLength)], d.PadValue)
+
+ return nil
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *DiscoverySolicit) Decode(data []byte) error {
+ i := 0
+ d.Opcode = data[i]
+ i++
+ d.DiscoveryType = data[i]
+ i++
+ d.VendorType = data[i]
+ i++
+ d.Length = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+
+ d.VendorIDType = data[i]
+ i++
+ d.VendorIDLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.VendorID = data[i : i+int(d.VendorIDLength)]
+ i = i + int(d.VendorIDLength)
+
+ d.CPType = data[i]
+ i++
+ d.CPLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.CPValue = data[i : i+int(d.CPLength)]
+ i = i + int(d.CPLength)
+
+ d.NWType = data[i]
+ i++
+ d.NWLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.NWValue = data[i : i+int(d.NWLength)]
+ i = i + int(d.NWLength)
+
+ d.DVType = data[i]
+ i++
+ d.DVLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.DVValue = data[i : i+int(d.DVLength)]
+ i = i + int(d.DVLength)
+
+ d.SCPType = data[i]
+ i++
+ d.SCPLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.SCPValue = data[i : i+int(d.SCPLength)]
+ i = i + int(d.SCPLength)
+
+ d.PadType = data[i]
+ i++
+ d.PadLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.PadValue = data[i : i+int(d.PadLength)]
+
+ return nil
+}
+
+// DiscoveryHello is a structure for Discovery message
+type DiscoveryHello struct {
+ layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16 // length of after this data without Padding
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ TunnelType uint8
+ TunnelLength uint16
+ TunnelValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+}
+
+// String returns the string expression of DiscoveryHello
+func (d *DiscoveryHello) String() string {
+ message := fmt.Sprintf("Opcode:%v, DiscoveryType:%v, VendorType:%v, Length:%v", d.Opcode, d.DiscoveryType, d.VendorType, d.Length)
+ message = fmt.Sprintf("%s, VendorIDType:%v, VendorIDLength:%v, VendorID:%v", message, d.VendorIDType, d.VendorIDLength, hex.EncodeToString(d.VendorID))
+ message = fmt.Sprintf("%s, NWType:%v, NWLength:%v, NWValue:%v", message, d.NWType, d.NWLength, hex.EncodeToString(d.NWValue))
+ message = fmt.Sprintf("%s, DVType:%v, DVLength:%v, DVValue:%v", message, d.DVType, d.DVLength, hex.EncodeToString(d.DVValue))
+ message = fmt.Sprintf("%s, SCPType:%v, SCPLength:%v, SCPValue:%v", message, d.SCPType, d.SCPLength, hex.EncodeToString(d.SCPValue))
+ message = fmt.Sprintf("%s, TunnelType:%v, TunnelLength:%v, TunnelValue:%v", message, d.TunnelType, d.TunnelLength, hex.EncodeToString(d.TunnelValue))
+ message = fmt.Sprintf("%s, PadType:%v, PadLength:%v, PadValue:%v", message, d.PadType, d.PadLength, hex.EncodeToString(d.PadValue))
+ return message
+}
+
+// Len returns the length of DiscoveryHello
+func (d *DiscoveryHello) Len() int {
+ len := (2) + (3) + int(d.Length) + (int(d.PadLength) + 3)
+ return len
+}
+
+// LayerType returns the ethernet type of DiscoveryHello
+func (d *DiscoveryHello) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *DiscoveryHello) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ data[i] = byte(d.DiscoveryType)
+ i++
+ data[i] = byte(d.VendorType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Length)
+ i = i + 2
+
+ data[i] = byte(d.VendorIDType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VendorIDLength)
+ i = i + 2
+ copy(data[i:i+int(d.VendorIDLength)], d.VendorID)
+ i = i + int(d.VendorIDLength)
+
+ data[i] = byte(d.NWType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.NWLength)
+ i = i + 2
+ copy(data[i:i+int(d.NWLength)], d.NWValue)
+ i = i + int(d.NWLength)
+
+ data[i] = byte(d.DVType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.DVLength)
+ i = i + 2
+ copy(data[i:i+int(d.DVLength)], d.DVValue)
+ i = i + int(d.DVLength)
+
+ data[i] = byte(d.SCPType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.SCPLength)
+ i = i + 2
+ copy(data[i:i+int(d.SCPLength)], d.SCPValue)
+ i = i + int(d.SCPLength)
+
+ data[i] = byte(d.TunnelType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.TunnelLength)
+ i = i + 2
+ copy(data[i:i+int(d.TunnelLength)], d.TunnelValue)
+ i = i + int(d.TunnelLength)
+
+ data[i] = byte(d.PadType)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.PadLength)
+ i = i + 2
+ copy(data[i:i+int(d.PadLength)], d.PadValue)
+
+ return nil
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *DiscoveryHello) Decode(data []byte) error {
+ i := 0
+ d.Opcode = data[i]
+ i++
+ d.DiscoveryType = data[i]
+ i++
+ d.VendorType = data[i]
+ i++
+ d.Length = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+
+ d.VendorIDType = data[i]
+ i++
+ d.VendorIDLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.VendorID = data[i : i+int(d.VendorIDLength)]
+ i = i + int(d.VendorIDLength)
+
+ d.NWType = data[i]
+ i++
+ d.NWLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.NWValue = data[i : i+int(d.NWLength)]
+ i = i + int(d.NWLength)
+
+ d.DVType = data[i]
+ i++
+ d.DVLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.DVValue = data[i : i+int(d.DVLength)]
+ i = i + int(d.DVLength)
+
+ d.SCPType = data[i]
+ i++
+ d.SCPLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.SCPValue = data[i : i+int(d.SCPLength)]
+ i = i + int(d.SCPLength)
+
+ d.TunnelType = data[i]
+ i++
+ d.TunnelLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.TunnelValue = data[i : i+int(d.TunnelLength)]
+ i = i + int(d.TunnelLength)
+
+ d.PadType = data[i]
+ i++
+ d.PadLength = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.PadValue = data[i : i+int(d.PadLength)]
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/msg_discovery_test.go b/internal/pkg/core/l2oam/msg_discovery_test.go
new file mode 100644
index 0000000..618c81a
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_discovery_test.go
@@ -0,0 +1,1409 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "reflect"
+ "testing"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+func TestGenerateDiscoverySOLICIT(t *testing.T) {
+ tests := []struct {
+ name string
+ want gopacket.SerializableLayer
+ }{
+ // TODO: Add test cases.
+ {
+ name: "GenerateDiscoverySOLICIT-1",
+ want: &DiscoverySolicit{
+ // IEEE 1904.2
+ Opcode: 0xfd,
+ DiscoveryType: 0x01,
+ // Vendor-specific
+ VendorType: 0xfe,
+ Length: 37,
+ // Vendor ID
+ VendorIDType: 0xfd,
+ VendorIDLength: 3,
+ VendorID: []byte{0x2a, 0xea, 0x15},
+ // Controller Priority
+ CPType: 0x05,
+ CPLength: 1,
+ CPValue: []byte{128},
+ //NetworkID
+ NWType: 0x06,
+ NWLength: 16,
+ NWValue: []byte("tibitcom.com"),
+ //Device Type
+ DVType: 0x07,
+ DVLength: 1,
+ DVValue: []byte{1},
+ //Supported CLient Protocols
+ SCPType: 0x08,
+ SCPLength: 1,
+ SCPValue: []byte{0x03},
+ //Padding
+ PadType: 0xff,
+ PadLength: 1,
+ PadValue: []byte{0},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := GenerateDiscoverySOLICIT(); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("GenerateDiscoverySOLICIT() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestDiscoverySolicit_String(t *testing.T) {
+ type fields struct {
+ BaseLayer layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ CPType uint8
+ CPLength uint16
+ CPValue []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want string
+ }{
+ // TODO: Add test cases.
+ {
+ name: "String-1",
+ fields: fields{
+ // IEEE 1904.2
+ Opcode: 0xfd,
+ DiscoveryType: 0x01,
+ // Vendor-specific
+ VendorType: 0xfe,
+ Length: 37,
+ // Vendor ID
+ VendorIDType: 0xfd,
+ VendorIDLength: 3,
+ VendorID: []byte{0x2a, 0xea, 0x15},
+ // Controller Priority
+ CPType: 0x05,
+ CPLength: 1,
+ CPValue: []byte{128},
+ //NetworkID
+ NWType: 0x06,
+ NWLength: 16,
+ NWValue: []byte("tibitcom.com"),
+ //Device Type
+ DVType: 0x07,
+ DVLength: 1,
+ DVValue: []byte{1},
+ //Supported CLient Protocols
+ SCPType: 0x08,
+ SCPLength: 1,
+ SCPValue: []byte{0x03},
+ //Padding
+ PadType: 0xff,
+ PadLength: 1,
+ PadValue: []byte{0},
+ },
+ want: fmt.Sprintf("Opcode:253, DiscoveryType:1, VendorType:254, Length:37, VendorIDType:253, VendorIDLength:3, VendorID:2aea15, CPType:5, CPLength:1, CPValue:80, NWType:6, NWLength:16, NWValue:%v, DVType:7, DVLength:1, DVValue:01, SCPType:8, SCPLength:1, SCPValue:03", hex.EncodeToString([]byte("tibitcom.com"))),
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ d := &DiscoverySolicit{
+ BaseLayer: tt.fields.BaseLayer,
+ Opcode: tt.fields.Opcode,
+ DiscoveryType: tt.fields.DiscoveryType,
+ VendorType: tt.fields.VendorType,
+ Length: tt.fields.Length,
+ VendorIDType: tt.fields.VendorIDType,
+ VendorIDLength: tt.fields.VendorIDLength,
+ VendorID: tt.fields.VendorID,
+ CPType: tt.fields.CPType,
+ CPLength: tt.fields.CPLength,
+ CPValue: tt.fields.CPValue,
+ NWType: tt.fields.NWType,
+ NWLength: tt.fields.NWLength,
+ NWValue: tt.fields.NWValue,
+ DVType: tt.fields.DVType,
+ DVLength: tt.fields.DVLength,
+ DVValue: tt.fields.DVValue,
+ SCPType: tt.fields.SCPType,
+ SCPLength: tt.fields.SCPLength,
+ SCPValue: tt.fields.SCPValue,
+ PadType: tt.fields.PadType,
+ PadLength: tt.fields.PadLength,
+ PadValue: tt.fields.PadValue,
+ }
+ if got := d.String(); got != tt.want {
+ t.Errorf("DiscoverySolicit.String() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestDiscoverySolicit_Len(t *testing.T) {
+ type fields struct {
+ BaseLayer layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ CPType uint8
+ CPLength uint16
+ CPValue []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want int
+ }{
+ // TODO: Add test cases.
+ {
+ name: "Len-1",
+ fields: fields{
+ // IEEE 1904.2
+ Opcode: 0xfd,
+ DiscoveryType: 0x01,
+ // Vendor-specific
+ VendorType: 0xfe,
+ Length: 37,
+ // Vendor ID
+ VendorIDType: 0xfd,
+ VendorIDLength: 3,
+ VendorID: []byte{0x2a, 0xea, 0x15},
+ // Controller Priority
+ CPType: 0x05,
+ CPLength: 1,
+ CPValue: []byte{128},
+ //NetworkID
+ NWType: 0x06,
+ NWLength: 16,
+ NWValue: []byte("tibitcom.com"),
+ //Device Type
+ DVType: 0x07,
+ DVLength: 1,
+ DVValue: []byte{1},
+ //Supported CLient Protocols
+ SCPType: 0x08,
+ SCPLength: 1,
+ SCPValue: []byte{0x03},
+ //Padding
+ PadType: 0xff,
+ PadLength: 1,
+ PadValue: []byte{0},
+ },
+ want: 46,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ d := &DiscoverySolicit{
+ BaseLayer: tt.fields.BaseLayer,
+ Opcode: tt.fields.Opcode,
+ DiscoveryType: tt.fields.DiscoveryType,
+ VendorType: tt.fields.VendorType,
+ Length: tt.fields.Length,
+ VendorIDType: tt.fields.VendorIDType,
+ VendorIDLength: tt.fields.VendorIDLength,
+ VendorID: tt.fields.VendorID,
+ CPType: tt.fields.CPType,
+ CPLength: tt.fields.CPLength,
+ CPValue: tt.fields.CPValue,
+ NWType: tt.fields.NWType,
+ NWLength: tt.fields.NWLength,
+ NWValue: tt.fields.NWValue,
+ DVType: tt.fields.DVType,
+ DVLength: tt.fields.DVLength,
+ DVValue: tt.fields.DVValue,
+ SCPType: tt.fields.SCPType,
+ SCPLength: tt.fields.SCPLength,
+ SCPValue: tt.fields.SCPValue,
+ PadType: tt.fields.PadType,
+ PadLength: tt.fields.PadLength,
+ PadValue: tt.fields.PadValue,
+ }
+ if got := d.Len(); got != tt.want {
+ t.Errorf("DiscoverySolicit.Len() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestDiscoverySolicit_LayerType(t *testing.T) {
+ type fields struct {
+ BaseLayer layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ CPType uint8
+ CPLength uint16
+ CPValue []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want gopacket.LayerType
+ }{
+ // TODO: Add test cases.
+ {
+ name: "LayerType-1",
+ want: layers.LayerTypeEthernet,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ d := &DiscoverySolicit{
+ BaseLayer: tt.fields.BaseLayer,
+ Opcode: tt.fields.Opcode,
+ DiscoveryType: tt.fields.DiscoveryType,
+ VendorType: tt.fields.VendorType,
+ Length: tt.fields.Length,
+ VendorIDType: tt.fields.VendorIDType,
+ VendorIDLength: tt.fields.VendorIDLength,
+ VendorID: tt.fields.VendorID,
+ CPType: tt.fields.CPType,
+ CPLength: tt.fields.CPLength,
+ CPValue: tt.fields.CPValue,
+ NWType: tt.fields.NWType,
+ NWLength: tt.fields.NWLength,
+ NWValue: tt.fields.NWValue,
+ DVType: tt.fields.DVType,
+ DVLength: tt.fields.DVLength,
+ DVValue: tt.fields.DVValue,
+ SCPType: tt.fields.SCPType,
+ SCPLength: tt.fields.SCPLength,
+ SCPValue: tt.fields.SCPValue,
+ PadType: tt.fields.PadType,
+ PadLength: tt.fields.PadLength,
+ PadValue: tt.fields.PadValue,
+ }
+ if got := d.LayerType(); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("DiscoverySolicit.LayerType() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestDiscoverySolicit_SerializeTo(t *testing.T) {
+ type fields struct {
+ BaseLayer layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ CPType uint8
+ CPLength uint16
+ CPValue []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+ }
+ type args struct {
+ b gopacket.SerializeBuffer
+ opts gopacket.SerializeOptions
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {
+ name: "SerializeTo-1",
+ fields: fields{
+ // IEEE 1904.2
+ Opcode: 0xfd,
+ DiscoveryType: 0x01,
+ // Vendor-specific
+ VendorType: 0xfe,
+ Length: 37,
+ // Vendor ID
+ VendorIDType: 0xfd,
+ VendorIDLength: 3,
+ VendorID: []byte{0x2a, 0xea, 0x15},
+ // Controller Priority
+ CPType: 0x05,
+ CPLength: 1,
+ CPValue: []byte{128},
+ //NetworkID
+ NWType: 0x06,
+ NWLength: 16,
+ NWValue: []byte("tibitcom.com"),
+ //Device Type
+ DVType: 0x07,
+ DVLength: 1,
+ DVValue: []byte{1},
+ //Supported CLient Protocols
+ SCPType: 0x08,
+ SCPLength: 1,
+ SCPValue: []byte{0x03},
+ //Padding
+ PadType: 0xff,
+ PadLength: 1,
+ PadValue: []byte{0},
+ },
+ args: args{
+ b: gopacket.NewSerializeBufferExpectedSize(0, 36),
+ opts: gopacket.SerializeOptions{},
+ },
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ d := &DiscoverySolicit{
+ BaseLayer: tt.fields.BaseLayer,
+ Opcode: tt.fields.Opcode,
+ DiscoveryType: tt.fields.DiscoveryType,
+ VendorType: tt.fields.VendorType,
+ Length: tt.fields.Length,
+ VendorIDType: tt.fields.VendorIDType,
+ VendorIDLength: tt.fields.VendorIDLength,
+ VendorID: tt.fields.VendorID,
+ CPType: tt.fields.CPType,
+ CPLength: tt.fields.CPLength,
+ CPValue: tt.fields.CPValue,
+ NWType: tt.fields.NWType,
+ NWLength: tt.fields.NWLength,
+ NWValue: tt.fields.NWValue,
+ DVType: tt.fields.DVType,
+ DVLength: tt.fields.DVLength,
+ DVValue: tt.fields.DVValue,
+ SCPType: tt.fields.SCPType,
+ SCPLength: tt.fields.SCPLength,
+ SCPValue: tt.fields.SCPValue,
+ PadType: tt.fields.PadType,
+ PadLength: tt.fields.PadLength,
+ PadValue: tt.fields.PadValue,
+ }
+ if err := d.SerializeTo(tt.args.b, tt.args.opts); (err != nil) != tt.wantErr {
+ t.Errorf("DiscoverySolicit.SerializeTo error = %v, wantErr %v", err, tt.wantErr)
+ }
+
+ data := tt.args.b.Bytes()
+ cnt := 0
+ digits := 0
+ if !reflect.DeepEqual(d.Opcode, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo Opcode error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.DiscoveryType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo DiscoveryType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.VendorType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo VendorType error")
+ }
+ cnt++
+
+ if !reflect.DeepEqual(d.Length, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo Length error")
+ }
+ cnt += 2
+
+ if !reflect.DeepEqual(d.VendorIDType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo VendorIDType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.VendorIDLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo VendorIDLength error")
+ }
+ cnt += 2
+ digits = int(d.VendorIDLength)
+ if !reflect.DeepEqual(d.VendorID, data[cnt:cnt+digits]) {
+ t.Error("DiscoverySolicit.SerializeTo VendorID error")
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.CPType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo CPType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.CPLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo CPLength error")
+ }
+ cnt += 2
+ digits = int(d.CPLength)
+ if !reflect.DeepEqual(d.CPValue, data[cnt:cnt+digits]) {
+ t.Error("DiscoverySolicit.SerializeTo CPValue error")
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.NWType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo NWType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.NWLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo NWLength error")
+ }
+ cnt += 2
+ //digits = int(d.NWLength)
+ for _, dvalue := range d.NWValue {
+ if !reflect.DeepEqual(dvalue, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo NWValue error")
+ }
+ cnt++
+ }
+ // if !reflect.DeepEqual(string(d.NWValue), string(data[cnt:cnt+digits])) {
+ // t.Errorf("DiscoverySolicit.SerializeTo NWValue error %x %x", d.NWValue, data[cnt:cnt+digits])
+ // }
+ cnt += 4
+ if !reflect.DeepEqual(d.DVType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo DVType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.DVLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo DVLength error")
+ }
+ cnt += 2
+ digits = int(d.DVLength)
+ if !reflect.DeepEqual(d.DVValue, data[cnt:cnt+digits]) {
+ t.Error("DiscoverySolicit.SerializeTo DVValue error")
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.SCPType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo SCPType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.SCPLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo SCPLength error")
+ }
+ cnt += 2
+ digits = int(d.SCPLength)
+ if !reflect.DeepEqual(d.SCPValue, data[cnt:cnt+digits]) {
+ t.Error("DiscoverySolicit.SerializeTo SCPValue error")
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.PadType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo PadType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.PadLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo PadLength error")
+ }
+ cnt += 2
+ digits = int(d.PadLength)
+ if !reflect.DeepEqual(d.PadValue, data[cnt:cnt+digits]) {
+ t.Error("DiscoverySolicit.SerializeTo PadValue error")
+ }
+
+ })
+ }
+}
+
+func TestDiscoverySolicit_Decode(t *testing.T) {
+ type fields struct {
+ BaseLayer layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ CPType uint8
+ CPLength uint16
+ CPValue []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+ }
+ type args struct {
+ data []byte
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {
+ name: "Decode-1",
+ args: args{
+ data: []byte{0xfd, 0x01, 0xfe, 0x00, 0x25, 0xfd, 0x00, 0x03, 0x2a, 0xea, 0x15, 0x05, 0x00, 0x01, 0x80, 0x06, 0x00, 0x10, 0x74, 0x69, 0x62, 0x69, 0x74, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, 0x03, 0xff, 0x00, 0x01, 0x00},
+ },
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ d := &DiscoverySolicit{
+ BaseLayer: tt.fields.BaseLayer,
+ Opcode: tt.fields.Opcode,
+ DiscoveryType: tt.fields.DiscoveryType,
+ VendorType: tt.fields.VendorType,
+ Length: tt.fields.Length,
+ VendorIDType: tt.fields.VendorIDType,
+ VendorIDLength: tt.fields.VendorIDLength,
+ VendorID: tt.fields.VendorID,
+ CPType: tt.fields.CPType,
+ CPLength: tt.fields.CPLength,
+ CPValue: tt.fields.CPValue,
+ NWType: tt.fields.NWType,
+ NWLength: tt.fields.NWLength,
+ NWValue: tt.fields.NWValue,
+ DVType: tt.fields.DVType,
+ DVLength: tt.fields.DVLength,
+ DVValue: tt.fields.DVValue,
+ SCPType: tt.fields.SCPType,
+ SCPLength: tt.fields.SCPLength,
+ SCPValue: tt.fields.SCPValue,
+ PadType: tt.fields.PadType,
+ PadLength: tt.fields.PadLength,
+ PadValue: tt.fields.PadValue,
+ }
+ if err := d.Decode(tt.args.data); (err != nil) != tt.wantErr {
+ t.Errorf("DiscoverySolicit.Decode error = %v, wantErr %v", err, tt.wantErr)
+ }
+
+ cnt := 0
+ digits := 0
+
+ if !reflect.DeepEqual(d.Opcode, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode Opcode error Opcode=%x,data=%x,cnt=%x", d.Opcode, tt.args.data[cnt:], cnt)
+ }
+ cnt++
+ if !reflect.DeepEqual(d.DiscoveryType, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode DiscoveryType error DiscoveryType=%X data=%x", d.DiscoveryType, tt.args.data[cnt:cnt+1])
+ }
+ cnt++
+ if !reflect.DeepEqual(d.VendorType, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode VendorType error %x %x", tt.args.data[cnt], d.VendorType)
+ }
+ cnt++
+
+ if !reflect.DeepEqual(d.Length, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Errorf("DiscoverySolicit.Decode Length error Length=%x data[cnt:cnt+2]=%x", string(d.Length), string(binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])))
+ }
+ cnt += 2
+
+ if !reflect.DeepEqual(d.VendorIDType, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode VendorIDType error VendorIDType=%x data=%x", d.VendorIDType, tt.args.data[cnt])
+ }
+ cnt++
+ if !reflect.DeepEqual(d.VendorIDLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Errorf("DiscoverySolicit.Decode VendorIDLength error VendorIDLength=%x data[cnt:cnt+2]=%x", d.VendorIDLength, tt.args.data[cnt:cnt+2])
+ }
+ cnt += 2
+ digits = int(d.VendorIDLength)
+ if !reflect.DeepEqual(d.VendorID, tt.args.data[cnt:cnt+digits]) {
+ t.Errorf("DiscoverySolicit.Decode VendorID error %v %v", d.VendorID, tt.args.data[cnt:cnt+3])
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.CPType, tt.args.data[cnt]) {
+ t.Error("DiscoverySolicit.Decode CPType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.CPLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.Decode CPLength error")
+ }
+ cnt += 2
+ digits = int(d.CPLength)
+ if !reflect.DeepEqual(d.CPValue, tt.args.data[cnt:cnt+digits]) {
+ t.Errorf("DiscoverySolicit.Decode CPValue error %x %x", tt.args.data[14:15], d.CPValue)
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.NWType, tt.args.data[cnt]) {
+ t.Error("DiscoverySolicit.Decode NWType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.NWLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.Decode NWLength error")
+ }
+ cnt += 2
+ //digits = int(d.NWLength)
+ for _, dvalue := range d.NWValue {
+ if !reflect.DeepEqual(dvalue, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode NWValue error %x %x", dvalue, tt.args.data[cnt])
+ }
+ cnt++
+ }
+ // if !reflect.DeepEqual(string(d.NWValue), string(tt.args.data[cnt:cnt+digits])) {
+ // t.Errorf("DiscoverySolicit.Decode NWValue error %x %x", d.NWValue, tt.args.data[cnt:cnt+digits])
+ // }
+ // cnt += 4
+ if !reflect.DeepEqual(d.DVType, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode DVType error %x %x", d.DVType, tt.args.data[cnt])
+ }
+ cnt++
+ if !reflect.DeepEqual(d.DVLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Errorf("DiscoverySolicit.Decode DVLength error %x %x", d.DVLength, tt.args.data[cnt:cnt+2])
+ }
+ cnt += 2
+ digits = int(d.DVLength)
+ if !reflect.DeepEqual(d.DVValue, tt.args.data[cnt:cnt+digits]) {
+ t.Errorf("DiscoverySolicit.Decode DVValue error %x %x", d.DVValue, tt.args.data[cnt:cnt+digits])
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.SCPType, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode SCPType error %x %x", d.SCPType, tt.args.data[cnt])
+ }
+ cnt++
+ if !reflect.DeepEqual(d.SCPLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.Decode SCPLength error")
+ }
+ cnt += 2
+ digits = int(d.SCPLength)
+ if !reflect.DeepEqual(d.SCPValue, tt.args.data[cnt:cnt+digits]) {
+ t.Errorf("DiscoverySolicit.Decode SCPValue error cnt= %x digits=%x", cnt, digits)
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.PadType, tt.args.data[cnt]) {
+ t.Error("DiscoverySolicit.Decode PadType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.PadLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.Decode PadLength error")
+ }
+ cnt += 2
+ digits = int(d.PadLength)
+ if !reflect.DeepEqual(d.PadValue, tt.args.data[cnt:cnt+digits]) {
+ t.Errorf("DiscoverySolicit.Decode PadValue error %x %x", d.PadValue, tt.args.data[cnt:cnt+digits])
+ }
+
+ })
+ }
+}
+
+func TestDiscoveryHello_String(t *testing.T) {
+ type fields struct {
+ BaseLayer layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ TunnelType uint8
+ TunnelLength uint16
+ TunnelValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want string
+ }{
+ // TODO: Add test cases.
+ {
+ name: "String-1",
+ fields: fields{
+ Opcode: 0xfd,
+ DiscoveryType: 0x01,
+ VendorType: 0xfe,
+ Length: 0x25,
+ VendorIDType: 0xfd,
+ VendorIDLength: 3,
+ VendorID: []byte{0x2a, 0xea, 0x15},
+ NWType: 0x06,
+ NWLength: 16,
+ NWValue: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
+ DVType: 0x07,
+ DVLength: 1,
+ DVValue: []byte{2},
+ SCPType: 0x08,
+ SCPLength: 1,
+ SCPValue: []byte{0x03},
+ TunnelType: 0x0a,
+ TunnelLength: 2,
+ TunnelValue: []byte{0x00, 0x01},
+ PadType: 0xff,
+ PadLength: 1,
+ PadValue: []byte{0},
+ },
+ want: "Opcode:253, DiscoveryType:1, VendorType:254, Length:37, VendorIDType:253, VendorIDLength:3, VendorID:2aea15, NWType:6, NWLength:16, NWValue:01020304050607080910111213141516, DVType:7, DVLength:1, DVValue:02, SCPType:8, SCPLength:1, SCPValue:03, TunnelType:10, TunnelLength:2, TunnelValue:0001, PadType:255, PadLength:1, PadValue:00",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ d := &DiscoveryHello{
+ BaseLayer: tt.fields.BaseLayer,
+ Opcode: tt.fields.Opcode,
+ DiscoveryType: tt.fields.DiscoveryType,
+ VendorType: tt.fields.VendorType,
+ Length: tt.fields.Length,
+ VendorIDType: tt.fields.VendorIDType,
+ VendorIDLength: tt.fields.VendorIDLength,
+ VendorID: tt.fields.VendorID,
+ NWType: tt.fields.NWType,
+ NWLength: tt.fields.NWLength,
+ NWValue: tt.fields.NWValue,
+ DVType: tt.fields.DVType,
+ DVLength: tt.fields.DVLength,
+ DVValue: tt.fields.DVValue,
+ SCPType: tt.fields.SCPType,
+ SCPLength: tt.fields.SCPLength,
+ SCPValue: tt.fields.SCPValue,
+ TunnelType: tt.fields.TunnelType,
+ TunnelLength: tt.fields.TunnelLength,
+ TunnelValue: tt.fields.TunnelValue,
+ PadType: tt.fields.PadType,
+ PadLength: tt.fields.PadLength,
+ PadValue: tt.fields.PadValue,
+ }
+ if got := d.String(); got != tt.want {
+ t.Errorf("DiscoveryHello.String() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestDiscoveryHello_Len(t *testing.T) {
+ type fields struct {
+ BaseLayer layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ TunnelType uint8
+ TunnelLength uint16
+ TunnelValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want int
+ }{
+ // TODO: Add test cases.
+ {
+ name: "Len-1",
+ fields: fields{
+ Opcode: 0xfd,
+ DiscoveryType: 0x01,
+ VendorType: 0xfe,
+ Length: 0x25,
+ VendorIDType: 0xfd,
+ VendorIDLength: 3,
+ VendorID: []byte{0x2a, 0xea, 0x15},
+ NWType: 0x06,
+ NWLength: 16,
+ NWValue: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
+ DVType: 0x07,
+ DVLength: 1,
+ DVValue: []byte{2},
+ SCPType: 0x08,
+ SCPLength: 1,
+ SCPValue: []byte{0x03},
+ TunnelType: 0x0a,
+ TunnelLength: 2,
+ TunnelValue: []byte{0x00, 0x01},
+ PadType: 0xff,
+ PadLength: 1,
+ PadValue: []byte{0},
+ },
+ want: 46,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ d := &DiscoveryHello{
+ BaseLayer: tt.fields.BaseLayer,
+ Opcode: tt.fields.Opcode,
+ DiscoveryType: tt.fields.DiscoveryType,
+ VendorType: tt.fields.VendorType,
+ Length: tt.fields.Length,
+ VendorIDType: tt.fields.VendorIDType,
+ VendorIDLength: tt.fields.VendorIDLength,
+ VendorID: tt.fields.VendorID,
+ NWType: tt.fields.NWType,
+ NWLength: tt.fields.NWLength,
+ NWValue: tt.fields.NWValue,
+ DVType: tt.fields.DVType,
+ DVLength: tt.fields.DVLength,
+ DVValue: tt.fields.DVValue,
+ SCPType: tt.fields.SCPType,
+ SCPLength: tt.fields.SCPLength,
+ SCPValue: tt.fields.SCPValue,
+ TunnelType: tt.fields.TunnelType,
+ TunnelLength: tt.fields.TunnelLength,
+ TunnelValue: tt.fields.TunnelValue,
+ PadType: tt.fields.PadType,
+ PadLength: tt.fields.PadLength,
+ PadValue: tt.fields.PadValue,
+ }
+ if got := d.Len(); got != tt.want {
+ t.Errorf("DiscoveryHello.Len() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestDiscoveryHello_LayerType(t *testing.T) {
+ type fields struct {
+ BaseLayer layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ TunnelType uint8
+ TunnelLength uint16
+ TunnelValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want gopacket.LayerType
+ }{
+ // TODO: Add test cases.
+ {
+ name: "LayerType-1",
+ want: layers.LayerTypeEthernet,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ d := &DiscoveryHello{
+ BaseLayer: tt.fields.BaseLayer,
+ Opcode: tt.fields.Opcode,
+ DiscoveryType: tt.fields.DiscoveryType,
+ VendorType: tt.fields.VendorType,
+ Length: tt.fields.Length,
+ VendorIDType: tt.fields.VendorIDType,
+ VendorIDLength: tt.fields.VendorIDLength,
+ VendorID: tt.fields.VendorID,
+ NWType: tt.fields.NWType,
+ NWLength: tt.fields.NWLength,
+ NWValue: tt.fields.NWValue,
+ DVType: tt.fields.DVType,
+ DVLength: tt.fields.DVLength,
+ DVValue: tt.fields.DVValue,
+ SCPType: tt.fields.SCPType,
+ SCPLength: tt.fields.SCPLength,
+ SCPValue: tt.fields.SCPValue,
+ TunnelType: tt.fields.TunnelType,
+ TunnelLength: tt.fields.TunnelLength,
+ TunnelValue: tt.fields.TunnelValue,
+ PadType: tt.fields.PadType,
+ PadLength: tt.fields.PadLength,
+ PadValue: tt.fields.PadValue,
+ }
+ if got := d.LayerType(); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("DiscoveryHello.LayerType() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestDiscoveryHello_SerializeTo(t *testing.T) {
+ type fields struct {
+ BaseLayer layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ TunnelType uint8
+ TunnelLength uint16
+ TunnelValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+ }
+ type args struct {
+ b gopacket.SerializeBuffer
+ opts gopacket.SerializeOptions
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {
+ name: "SerializeTo-1",
+ fields: fields{
+ Opcode: 0xfd,
+ DiscoveryType: 0x01,
+ VendorType: 0xfe,
+ Length: 0x25,
+ VendorIDType: 0xfd,
+ VendorIDLength: 3,
+ VendorID: []byte{0x2a, 0xea, 0x15},
+ NWType: 0x06,
+ NWLength: 16,
+ NWValue: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
+ DVType: 0x07,
+ DVLength: 1,
+ DVValue: []byte{2},
+ SCPType: 0x08,
+ SCPLength: 1,
+ SCPValue: []byte{0x03},
+ TunnelType: 0x0a,
+ TunnelLength: 2,
+ TunnelValue: []byte{0x00, 0x01},
+ PadType: 0xff,
+ PadLength: 1,
+ PadValue: []byte{0},
+ },
+ args: args{
+ b: gopacket.NewSerializeBufferExpectedSize(0, 46),
+ opts: gopacket.SerializeOptions{},
+ },
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ d := &DiscoveryHello{
+ BaseLayer: tt.fields.BaseLayer,
+ Opcode: tt.fields.Opcode,
+ DiscoveryType: tt.fields.DiscoveryType,
+ VendorType: tt.fields.VendorType,
+ Length: tt.fields.Length,
+ VendorIDType: tt.fields.VendorIDType,
+ VendorIDLength: tt.fields.VendorIDLength,
+ VendorID: tt.fields.VendorID,
+ NWType: tt.fields.NWType,
+ NWLength: tt.fields.NWLength,
+ NWValue: tt.fields.NWValue,
+ DVType: tt.fields.DVType,
+ DVLength: tt.fields.DVLength,
+ DVValue: tt.fields.DVValue,
+ SCPType: tt.fields.SCPType,
+ SCPLength: tt.fields.SCPLength,
+ SCPValue: tt.fields.SCPValue,
+ TunnelType: tt.fields.TunnelType,
+ TunnelLength: tt.fields.TunnelLength,
+ TunnelValue: tt.fields.TunnelValue,
+ PadType: tt.fields.PadType,
+ PadLength: tt.fields.PadLength,
+ PadValue: tt.fields.PadValue,
+ }
+ if err := d.SerializeTo(tt.args.b, tt.args.opts); (err != nil) != tt.wantErr {
+ t.Errorf("DiscoveryHello.SerializeTo() error = %v, wantErr %v", err, tt.wantErr)
+ }
+
+ data := tt.args.b.Bytes()
+ cnt := 0
+ digits := 0
+ if !reflect.DeepEqual(d.Opcode, data[cnt]) {
+ t.Errorf("DiscoverySolicit.SerializeTo Opcode error Opcode=%x,data=%x,cnt=%x", d.Opcode, data[cnt:], cnt)
+ }
+ cnt++
+ if !reflect.DeepEqual(d.DiscoveryType, data[cnt]) {
+ t.Errorf("DiscoverySolicit.SerializeTo DiscoveryType error DiscoveryType=%X data=%x", d.DiscoveryType, data[cnt:cnt+1])
+ }
+ cnt++
+ if !reflect.DeepEqual(d.VendorType, data[cnt]) {
+ t.Errorf("DiscoverySolicit.SerializeTo VendorType error %x %x", data[cnt], d.VendorType)
+ }
+ cnt++
+
+ if !reflect.DeepEqual(d.Length, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Errorf("DiscoverySolicit.SerializeTo Length error Length=%x data[cnt:cnt+2]=%x", string(d.Length), string(binary.BigEndian.Uint16(data[cnt:cnt+2])))
+ }
+ cnt += 2
+
+ if !reflect.DeepEqual(d.VendorIDType, data[cnt]) {
+ t.Errorf("DiscoverySolicit.SerializeTo VendorIDType error VendorIDType=%x data=%x", d.VendorIDType, data[cnt])
+ }
+ cnt++
+ if !reflect.DeepEqual(d.VendorIDLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Errorf("DiscoverySolicit.SerializeTo VendorIDLength error VendorIDLength=%x data[cnt:cnt+2]=%x", d.VendorIDLength, data[cnt:cnt+2])
+ }
+ cnt += 2
+ digits = int(d.VendorIDLength)
+ if !reflect.DeepEqual(d.VendorID, data[cnt:cnt+digits]) {
+ t.Errorf("DiscoverySolicit.SerializeTo VendorID error %v %v", d.VendorID, data[cnt:cnt+3])
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.NWType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo NWType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.NWLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo NWLength error")
+ }
+ cnt += 2
+ //digits = int(d.NWLength)
+ for _, dvalue := range d.NWValue {
+ if !reflect.DeepEqual(dvalue, data[cnt]) {
+ t.Errorf("DiscoverySolicit.SerializeTo NWValue error %x %x", dvalue, data[cnt])
+ }
+ cnt++
+ }
+ // if !reflect.DeepEqual(string(d.NWValue), string(data[cnt:cnt+digits])) {
+ // t.Errorf("DiscoverySolicit.SerializeTo NWValue error %x %x", d.NWValue, data[cnt:cnt+digits])
+ // }
+ // cnt += 16
+ if !reflect.DeepEqual(d.DVType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo DVType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.DVLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo DVLength error")
+ }
+ cnt += 2
+ digits = int(d.DVLength)
+ if !reflect.DeepEqual(d.DVValue, data[cnt:cnt+digits]) {
+ t.Error("DiscoverySolicit.SerializeTo DVValue error")
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.SCPType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo SCPType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.SCPLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo SCPLength error")
+ }
+ cnt += 2
+ digits = int(d.SCPLength)
+ if !reflect.DeepEqual(d.SCPValue, data[cnt:cnt+digits]) {
+ t.Error("DiscoverySolicit.SerializeTo SCPValue error")
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.TunnelType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo TunnelType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.TunnelLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo TunnelLength error")
+ }
+ cnt += 2
+ digits = int(d.TunnelLength)
+ if !reflect.DeepEqual(d.TunnelValue, data[cnt:cnt+digits]) {
+ t.Error("DiscoverySolicit.SerializeTo TunnelValue error")
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.PadType, data[cnt]) {
+ t.Error("DiscoverySolicit.SerializeTo PadType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.PadLength, binary.BigEndian.Uint16(data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.SerializeTo PadLength error")
+ }
+ cnt += 2
+ digits = int(d.PadLength)
+ if !reflect.DeepEqual(d.PadValue, data[cnt:cnt+digits]) {
+ t.Errorf("DiscoverySolicit.SerializeTo PadValue error %x %x", d.PadValue, data[cnt:cnt+digits])
+ }
+ })
+ }
+}
+
+func TestDiscoveryHello_Decode(t *testing.T) {
+ type fields struct {
+ BaseLayer layers.BaseLayer
+ Opcode uint8
+ DiscoveryType uint8
+ VendorType uint8
+ Length uint16
+ VendorIDType uint8
+ VendorIDLength uint16
+ VendorID []byte
+ NWType uint8
+ NWLength uint16
+ NWValue []byte
+ DVType uint8
+ DVLength uint16
+ DVValue []byte
+ SCPType uint8
+ SCPLength uint16
+ SCPValue []byte
+ TunnelType uint8
+ TunnelLength uint16
+ TunnelValue []byte
+ PadType uint8
+ PadLength uint16
+ PadValue []byte
+ }
+ type args struct {
+ data []byte
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {
+ name: "Decode-1",
+ args: args{
+ data: []byte{0xfd, 0x02, 0xfe, 0x00, 0x26, 0xfd, 0x00, 0x03, 0x2a, 0xea, 0x15, 0x06, 0x00, 0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x07, 0x00, 0x01, 0x02, 0x08, 0x00, 0x01, 0x03, 0x0a, 0x00, 0x02, 0x00, 0x01, 0xff, 0x00, 0x01, 0x00},
+ },
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ d := &DiscoveryHello{
+ BaseLayer: tt.fields.BaseLayer,
+ Opcode: tt.fields.Opcode,
+ DiscoveryType: tt.fields.DiscoveryType,
+ VendorType: tt.fields.VendorType,
+ Length: tt.fields.Length,
+ VendorIDType: tt.fields.VendorIDType,
+ VendorIDLength: tt.fields.VendorIDLength,
+ VendorID: tt.fields.VendorID,
+ NWType: tt.fields.NWType,
+ NWLength: tt.fields.NWLength,
+ NWValue: tt.fields.NWValue,
+ DVType: tt.fields.DVType,
+ DVLength: tt.fields.DVLength,
+ DVValue: tt.fields.DVValue,
+ SCPType: tt.fields.SCPType,
+ SCPLength: tt.fields.SCPLength,
+ SCPValue: tt.fields.SCPValue,
+ TunnelType: tt.fields.TunnelType,
+ TunnelLength: tt.fields.TunnelLength,
+ TunnelValue: tt.fields.TunnelValue,
+ PadType: tt.fields.PadType,
+ PadLength: tt.fields.PadLength,
+ PadValue: tt.fields.PadValue,
+ }
+ if err := d.Decode(tt.args.data); (err != nil) != tt.wantErr {
+ t.Errorf("DiscoveryHello.Decode() error = %v, wantErr %v", err, tt.wantErr)
+ }
+
+ cnt := 0
+ digits := 0
+ if !reflect.DeepEqual(d.Opcode, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode Opcode error Opcode=%x,data=%x,cnt=%x", d.Opcode, tt.args.data[cnt:], cnt)
+ }
+ cnt++
+ if !reflect.DeepEqual(d.DiscoveryType, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode DiscoveryType error DiscoveryType=%X data=%x", d.DiscoveryType, tt.args.data[cnt:cnt+1])
+ }
+ cnt++
+ if !reflect.DeepEqual(d.VendorType, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode VendorType error %x %x", tt.args.data[cnt], d.VendorType)
+ }
+ cnt++
+
+ if !reflect.DeepEqual(d.Length, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Errorf("DiscoverySolicit.Decode Length error Length=%x data[cnt:cnt+2]=%x", string(d.Length), string(binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])))
+ }
+ cnt += 2
+
+ if !reflect.DeepEqual(d.VendorIDType, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode VendorIDType error VendorIDType=%x data=%x", d.VendorIDType, tt.args.data[cnt])
+ }
+ cnt++
+ if !reflect.DeepEqual(d.VendorIDLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Errorf("DiscoverySolicit.Decode VendorIDLength error VendorIDLength=%x data[cnt:cnt+2]=%x", d.VendorIDLength, tt.args.data[cnt:cnt+2])
+ }
+ cnt += 2
+ digits = int(d.VendorIDLength)
+ if !reflect.DeepEqual(d.VendorID, tt.args.data[cnt:cnt+digits]) {
+ t.Errorf("DiscoverySolicit.Decode VendorID error %v %v", d.VendorID, tt.args.data[cnt:cnt+3])
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.NWType, tt.args.data[cnt]) {
+ t.Error("DiscoverySolicit.Decode NWType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.NWLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.Decode NWLength error")
+ }
+ cnt += 2
+ //digits = int(d.NWLength)
+ for _, dvalue := range d.NWValue {
+ if !reflect.DeepEqual(dvalue, tt.args.data[cnt]) {
+ t.Errorf("DiscoverySolicit.Decode NWValue error %x %x", dvalue, tt.args.data[cnt])
+ }
+ cnt++
+ }
+ // if !reflect.DeepEqual(string(d.NWValue), string(tt.args.data[cnt:cnt+digits])) {
+ // t.Errorf("DiscoverySolicit.Decode NWValue error %x %x", d.NWValue, tt.args.data[cnt:cnt+digits])
+ // }
+ // cnt += 16
+ if !reflect.DeepEqual(d.DVType, tt.args.data[cnt]) {
+ t.Error("DiscoverySolicit.Decode DVType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.DVLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.Decode DVLength error")
+ }
+ cnt += 2
+ digits = int(d.DVLength)
+ if !reflect.DeepEqual(d.DVValue, tt.args.data[cnt:cnt+digits]) {
+ t.Error("DiscoverySolicit.Decode DVValue error")
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.SCPType, tt.args.data[cnt]) {
+ t.Error("DiscoverySolicit.Decode SCPType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.SCPLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.Decode SCPLength error")
+ }
+ cnt += 2
+ digits = int(d.SCPLength)
+ if !reflect.DeepEqual(d.SCPValue, tt.args.data[cnt:cnt+digits]) {
+ t.Error("DiscoverySolicit.Decode SCPValue error")
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.TunnelType, tt.args.data[cnt]) {
+ t.Error("DiscoverySolicit.Decode TunnelType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.TunnelLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.Decode TunnelLength error")
+ }
+ cnt += 2
+ digits = int(d.TunnelLength)
+ if !reflect.DeepEqual(d.TunnelValue, tt.args.data[cnt:cnt+digits]) {
+ t.Error("DiscoverySolicit.Decode TunnelValue error")
+ }
+ cnt += digits
+ if !reflect.DeepEqual(d.PadType, tt.args.data[cnt]) {
+ t.Error("DiscoverySolicit.Decode PadType error")
+ }
+ cnt++
+ if !reflect.DeepEqual(d.PadLength, binary.BigEndian.Uint16(tt.args.data[cnt:cnt+2])) {
+ t.Error("DiscoverySolicit.Decode PadLength error")
+ }
+ cnt += 2
+ digits = int(d.PadLength)
+ if !reflect.DeepEqual(d.PadValue, tt.args.data[cnt:cnt+digits]) {
+ t.Errorf("DiscoverySolicit.Decode PadValue error %x %x", d.PadValue, tt.args.data[cnt:cnt+digits])
+ }
+ })
+ }
+}
diff --git a/internal/pkg/core/l2oam/msg_generic_action_create.go b/internal/pkg/core/l2oam/msg_generic_action_create.go
new file mode 100644
index 0000000..a50c6a5
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_generic_action_create.go
@@ -0,0 +1,298 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+const (
+ ActionTypeProtocolFilter = 1
+
+ ActionTypeTrafficProfile = 2
+)
+
+// GenerateGenericActionCreate generates "Generic/Action Create" message
+func GenerateGenericActionCreate(actionType int) gopacket.SerializableLayer {
+ tibitData := &setGenericActionCreateReq{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OMI Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBiT OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0dce,
+ OCLength: 4,
+ OCInstance: 00000000,
+ //Vc
+ VcBranch: 0x6e,
+ VcLeaf: 0x7001,
+ VcLength: 4,
+ //OT
+ OT: []OTGenericActionCreate{{OTLength: 3, OTValue: getObjectType(actionType)}},
+ // End
+ EndBranch: 0,
+ }
+
+ return tibitData
+}
+
+func getObjectType(actionType int) []byte {
+ switch actionType {
+ case ActionTypeProtocolFilter:
+ return []byte{0x0c, 0x0c, 0xff}
+ case ActionTypeTrafficProfile:
+ return []byte{0x0c, 0x07, 0x0f}
+ default:
+ return []byte{0x00, 0x00, 0x00}
+ }
+}
+
+type setGenericActionCreateReq struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+ OT []OTGenericActionCreate
+ EndBranch uint8
+}
+
+// OTGenericActionCreate is a structure for "Object Type"
+type OTGenericActionCreate struct {
+ OTLength uint8
+ OTValue []byte
+}
+
+// String returns the string expression of setGenericActionCreateReq
+func (d *setGenericActionCreateReq) String() string {
+ message := fmt.Sprintf("Opcode:%x, Flags:%x, OAMPDUCode:%x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, CTBranch:%x, CTType:%x, CTLength:%x, CTInstance:%x", message, d.CTBranch, d.CTType, d.CTLength, d.CTInstance)
+ message = fmt.Sprintf("%s, OCBranch:%x, OCType:%x, OCLength:%x, OCInstance:%x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VcBranch:%x, VcLeaf:%x, VcLength:%x", message, d.VcBranch, d.VcLeaf, d.VcLength)
+ for _, ot := range d.OT {
+ message = fmt.Sprintf("%s, OTLength:%x, OTValue:%v", message, ot.OTLength, hex.EncodeToString(ot.OTValue))
+ }
+ message = fmt.Sprintf("%s, EndBranch:%x", message, d.EndBranch)
+ return message
+}
+
+// Len returns the length of setGenericActionCreateReq
+func (d *setGenericActionCreateReq) Len() int {
+ return 21 + int(d.CTLength) + int(d.OCLength) + int(d.VcLength)
+
+}
+
+// LayerType returns the ethernet type of setGenericActionCreateReq
+func (d *setGenericActionCreateReq) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *setGenericActionCreateReq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Flags)
+ i += 2
+ data[i] = byte(d.OAMPDUCode)
+ i++
+ copy(data[i:i+len(d.OUId)], d.OUId)
+ i += len(d.OUId)
+ data[i] = byte(d.TOMIOpcode)
+ i++
+ data[i] = byte(d.CTBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.CTType)
+ i += 2
+ data[i] = byte(d.CTLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.CTInstance)
+ i += 4
+ data[i] = byte(d.OCBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.OCType)
+ i += 2
+ data[i] = byte(d.OCLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.OCInstance)
+ i += 4
+ data[i] = byte(d.VcBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VcLeaf)
+ i += 2
+ data[i] = byte(d.VcLength)
+ i++
+ for _, ot := range d.OT {
+ nextIndex := serializeObjectType(&ot, data, i)
+ i = nextIndex
+ }
+
+ data[i] = byte(d.EndBranch)
+
+ return nil
+
+}
+
+func serializeObjectType(ot *OTGenericActionCreate, data []byte, startIndex int) int {
+ i := startIndex
+ data[i] = ot.OTLength
+ i++
+ copy(data[i:i+int(ot.OTLength)], ot.OTValue)
+ i += int(ot.OTLength)
+
+ return i
+
+}
+
+// SetGenericActionCreateRes is a structure for a response of "Generic/Action Create"
+type SetGenericActionCreateRes struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+
+ ObOCLength uint8
+ ObOCBranch uint8
+ ObOCType uint16
+ ObOCLength2 uint8
+ ObOCInstance []byte
+
+ EndBranch uint8
+}
+
+// ObOC is a structure for "Object OC"
+type ObOC struct {
+ ObOCLength uint8
+ ObOCBranch uint8
+ ObOCType uint16
+ ObOCLength2 uint8
+ ObOCInstance []byte
+}
+
+// String returns the string expression of SetGenericActionCreateRes
+func (d *SetGenericActionCreateRes) String() string {
+ message := fmt.Sprintf("Opcode:%x, Flags:%x, OAMPDUCode:%x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, CTBranch:%x, CTType:%x, CTLength:%x, CTInstance:%x", message, d.CTBranch, d.CTType, d.CTLength, d.CTInstance)
+ message = fmt.Sprintf("%s, OCBranch:%x, OCType:%x, OCLength:%x, OCInstance:%x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VcBranch:%x, VcLeaf:%x, VcLength:%x", message, d.VcBranch, d.VcLeaf, d.VcLength)
+ message = fmt.Sprintf("%s, ObOCLength:%x, ObOCBranch:%x, ObOCType:%x, ObOCLength2:%x, ObOCInstance:%x, EndBranch:%x",
+ message, d.ObOCLength, d.ObOCBranch, d.ObOCType, d.ObOCLength2, d.ObOCInstance, d.EndBranch)
+ return message
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *SetGenericActionCreateRes) Decode(data []byte) error {
+ i := 0
+ d.Opcode = data[i]
+ i++
+ d.Flags = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OAMPDUCode = data[i]
+ i++
+ d.OUId = data[i : i+3]
+ i += len(d.OUId)
+ d.TOMIOpcode = data[i]
+ i++
+ d.CTBranch = data[i]
+ i++
+ d.CTType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.CTLength = data[i]
+ i++
+ d.CTInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.OCBranch = data[i]
+ i++
+ d.OCType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OCLength = data[i]
+ i++
+ d.OCInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.VcBranch = data[i]
+ i++
+ d.VcLeaf = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.VcLength = data[i]
+ i++
+
+ d.ObOCLength = data[i]
+ i++
+ d.ObOCBranch = data[i]
+ i++
+ d.ObOCType = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.ObOCLength2 = data[i]
+ i++
+ d.ObOCInstance = data[i : i+int(d.ObOCLength2)]
+ i = i + int(d.ObOCLength2)
+
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// GetTrafficProfile returns the traffic profile of this message
+func (d *SetGenericActionCreateRes) GetTrafficProfile() []byte {
+ return d.ObOCInstance
+}
diff --git a/internal/pkg/core/l2oam/msg_generic_action_create_ds.go b/internal/pkg/core/l2oam/msg_generic_action_create_ds.go
new file mode 100644
index 0000000..40356d3
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_generic_action_create_ds.go
@@ -0,0 +1,57 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateGenericActionCreateDs generates "Generic/Action Create" message
+func GenerateGenericActionCreateDs() gopacket.SerializableLayer {
+
+ tibitData := &setGenericActionCreateReq{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OMI Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBiT OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0dce,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ //Vc
+ VcBranch: 0x6e,
+ VcLeaf: 0x7001,
+ VcLength: 4,
+ //OT
+ OT: []OTGenericActionCreate{{OTLength: 3, OTValue: []byte{0x0c, 0x02, 0x5d}}},
+ // End
+ EndBranch: 0,
+ }
+
+ return tibitData
+
+}
diff --git a/internal/pkg/core/l2oam/msg_generic_action_create_req.go b/internal/pkg/core/l2oam/msg_generic_action_create_req.go
new file mode 100644
index 0000000..766d1a1
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_generic_action_create_req.go
@@ -0,0 +1,35 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+
+ "github.com/google/gopacket"
+)
+
+func GenerateGenericActionCreateReq() gopacket.SerializableLayer {
+
+ data := &TibitFrame{
+ Data: []byte{0x03, 0x00, 0x50, 0xfe, 0x2a, 0xea, 0x15, 0x03, 0x0c, 0x0c, 0x7a, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0d, 0xce, 0x04, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x70, 0x01, 0x04, 0x03, 0x0c, 0x02, 0x5d, 0x00, 0x00},
+ }
+
+ binary.BigEndian.PutUint32(data.Data[12:16], getOltInstance())
+
+ return data
+
+}
diff --git a/internal/pkg/core/l2oam/msg_get_firmware_version.go b/internal/pkg/core/l2oam/msg_get_firmware_version.go
new file mode 100644
index 0000000..82995a7
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_get_firmware_version.go
@@ -0,0 +1,118 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+)
+
+// GenerateGetFirmwareVersion generates "Running Firmware Version" message
+func GenerateGetFirmwareVersion() gopacket.SerializableLayer {
+ tibitData := &TOAMGetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OMI Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBiT OLT Management Interface
+ TOMIOpcode: 0x01,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0dce,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vd
+ VdBranch: 0xde,
+ VdLeaf: 0x001b,
+ // End
+ EndBranch: 0,
+ }
+ return tibitData
+}
+
+// GetFirmwareVersionRes is a structure for a response of "Running Firmware Version"
+type GetFirmwareVersionRes struct {
+ ComResp TOAMGetResponse
+ AKLength uint8
+ AKValue []byte
+ RVLength uint8
+ RVValue []byte
+ BSLength uint8
+ BSValue []byte
+ RNLength uint8
+ RNValue []byte
+ BDLength uint8
+ BDValue []byte
+ EndBranch uint8
+}
+
+// String returns the string expression of GetFirmwareVersionRes
+func (d *GetFirmwareVersionRes) String() string {
+ message := d.ComResp.String()
+ message = fmt.Sprintf("%s, AKLength:%02x, AKValue:%s, RVLength:%02x, RVValue:%s", message, d.AKLength, hex.EncodeToString(d.AKValue), d.RVLength, hex.EncodeToString(d.RVValue))
+ message = fmt.Sprintf("%s, BSLength:%02x, BSValue:%s, RNLength:%02x, RNValue:%s", message, d.BSLength, hex.EncodeToString(d.BSValue), d.RNLength, hex.EncodeToString(d.RNValue))
+ message = fmt.Sprintf("%s, BDLength:%02x, BDValue:%s, EndBranch:%02x", message, d.BDLength, hex.EncodeToString(d.BDValue), d.EndBranch)
+ return message
+}
+
+// Len returns the length of GetFirmwareVersionRes
+func (d *GetFirmwareVersionRes) Len() int {
+ return d.ComResp.Len() + int(d.ComResp.VcLength) + 1
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *GetFirmwareVersionRes) Decode(data []byte) error {
+ d.ComResp.Decode(data)
+ i := d.ComResp.Len()
+ d.AKLength = data[i]
+ i++
+ d.AKValue = data[i : i+int(d.AKLength)]
+ i += int(d.AKLength)
+ d.RVLength = data[i]
+ i++
+ d.RVValue = data[i : i+int(d.RVLength)]
+ i += int(d.RVLength)
+ d.BSLength = data[i]
+ i++
+ d.BSValue = data[i : i+int(d.BSLength)]
+ i += int(d.BSLength)
+ d.RNLength = data[i]
+ i++
+ d.RNValue = data[i : i+int(d.RNLength)]
+ i += int(d.RNLength)
+ d.BDLength = data[i]
+ i++
+ d.BDValue = data[i : i+int(d.BDLength)]
+ i += int(d.BDLength)
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// GetFirmwareVersionNumber returns a firmware version number
+func (d *GetFirmwareVersionRes) GetFirmwareVersionNumber() string {
+ return string(d.RVValue)
+}
diff --git a/internal/pkg/core/l2oam/msg_get_mac_address.go b/internal/pkg/core/l2oam/msg_get_mac_address.go
new file mode 100644
index 0000000..e1df063
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_get_mac_address.go
@@ -0,0 +1,97 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/hex"
+ "fmt"
+ "strings"
+
+ "github.com/google/gopacket"
+)
+
+// GenerateGetMacAddress generates "PON Port/MAC Address" message
+func GenerateGetMacAddress() gopacket.SerializableLayer {
+ tibitData := &TOAMGetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x01,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0007,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vd
+ VdBranch: 0x07,
+ VdLeaf: 0x0004,
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
+
+// GetMacAddressRes is a structure for a toam response
+type GetMacAddressRes struct {
+ ComResp TOAMGetResponse
+ EcLength uint8
+ EcValue []byte
+ EndBranch uint8
+}
+
+// String returns the string expression of GetMacAddressRes
+func (d *GetMacAddressRes) String() string {
+ message := d.ComResp.String()
+ message = fmt.Sprintf("%s, EcLength:%x, EcValue:%v, EndBranch:%x", message, d.EcLength, hex.EncodeToString(d.EcValue), d.EndBranch)
+ return message
+}
+
+// Len returns the length of GetMacAddressRes
+func (d *GetMacAddressRes) Len() int {
+ return d.ComResp.Len() + int(d.ComResp.VcLength) + 1
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *GetMacAddressRes) Decode(data []byte) error {
+ d.ComResp.Decode(data)
+ i := d.ComResp.Len()
+ d.EcLength = data[i]
+ i++
+ d.EcValue = data[i : i+int(d.EcLength)]
+ i = i + int(d.EcLength)
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// GetMacAddress returns a MAC address
+func (d *GetMacAddressRes) GetMacAddress() string {
+ var buf []string
+ for _, b := range d.EcValue {
+ buf = append(buf, fmt.Sprintf("%02x", b))
+ }
+ return strings.Join(buf, ":")
+}
diff --git a/internal/pkg/core/l2oam/msg_get_manufacturer.go b/internal/pkg/core/l2oam/msg_get_manufacturer.go
new file mode 100644
index 0000000..74aa268
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_get_manufacturer.go
@@ -0,0 +1,94 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+)
+
+// GenerateManuFacturerInfo generates "Device/Manufacturer Info" message
+func GenerateManuFacturerInfo() gopacket.SerializableLayer {
+
+ tibitData := &TOAMGetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ //OAMPrtocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ //TiBitT OLT Management
+ TOMIOpcode: 0x01,
+ //Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ //Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0dce,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ //Vd
+ VdBranch: 0xde,
+ VdLeaf: 0x0006,
+ //End
+ EndBranch: 0x00,
+ }
+ return tibitData
+
+}
+
+// GetManufacturerRes is a structure for a toam response
+type GetManufacturerRes struct {
+ ComResp TOAMGetResponse
+ ECLength uint8
+ ECValue []byte
+ EndBranch uint8
+}
+
+// String returns the string expression of GetManufacturerRes
+func (d *GetManufacturerRes) String() string {
+ message := d.ComResp.String()
+ message = fmt.Sprintf("%s, ECLength:%02x, ECValue:%v,EndBranch:%02x", message, d.ECLength, hex.EncodeToString(d.ECValue), d.EndBranch)
+ return message
+}
+
+// Len returns the length of GetManufacturerRes
+func (d *GetManufacturerRes) Len() int {
+ return d.ComResp.Len() + int(d.ComResp.VcLength) + 1
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *GetManufacturerRes) Decode(data []byte) error {
+ d.ComResp.Decode(data)
+ i := d.ComResp.Len()
+ d.ECLength = data[i]
+ i++
+ d.ECValue = data[i : i+int(d.ECLength)]
+ i = i + int(d.ECLength)
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// GetManufacturer returns a manufacturer info.
+func (d *GetManufacturerRes) GetManufacturer() string {
+ return string(d.ECValue)
+}
diff --git a/internal/pkg/core/l2oam/msg_get_module_number.go b/internal/pkg/core/l2oam/msg_get_module_number.go
new file mode 100644
index 0000000..b256acd
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_get_module_number.go
@@ -0,0 +1,92 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+)
+
+// GenerateGetModuleNumber generates "Device/Model Number" message
+func GenerateGetModuleNumber() gopacket.SerializableLayer {
+ tibitData := &TOAMGetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OMI Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBiT OLT Management Interface
+ TOMIOpcode: 0x81,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0dce,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vd
+ VdBranch: 0xde,
+ VdLeaf: 0x000a,
+ // End
+ EndBranch: 0,
+ }
+ return tibitData
+}
+
+// GetModuleNumberRes is a structure for toam message
+type GetModuleNumberRes struct {
+ ComResp TOAMGetResponse
+ EcLength uint8
+ EcValue []byte
+ EndBranch uint8
+}
+
+// String returns the string expression of GetModuleNumberRes
+func (d *GetModuleNumberRes) String() string {
+ message := d.ComResp.String()
+ message = fmt.Sprintf("%s, EcLength:%02x, EcValue:%s EndBranch:%02x", message, d.EcLength, hex.EncodeToString(d.EcValue), d.EndBranch)
+ return message
+}
+
+// Len returns the length of GetModuleNumberRes
+func (d *GetModuleNumberRes) Len() int {
+ return d.ComResp.Len() + int(d.ComResp.VcLength) + 1
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *GetModuleNumberRes) Decode(data []byte) error {
+ d.ComResp.Decode(data)
+ i := d.ComResp.Len()
+ d.EcLength = data[i]
+ i++
+ d.EcValue = data[i : i+int(d.EcLength)]
+ i += int(d.EcLength)
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// GetModuleNumber returns a module number
+func (d *GetModuleNumberRes) GetModuleNumber() string {
+ return string(d.EcValue)
+}
diff --git a/internal/pkg/core/l2oam/msg_get_mpcp_llid.go b/internal/pkg/core/l2oam/msg_get_mpcp_llid.go
new file mode 100644
index 0000000..f1f7a10
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_get_mpcp_llid.go
@@ -0,0 +1,200 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// GetMpcpLLIdReq is a structure for "MPCP/LLID" message
+type GetMpcpLLIdReq struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VdBranch uint8
+ VdLeaf uint16
+ EndBranch uint8
+}
+
+// GenerateGetMpcpLLId generates "MPCP/LLID" message
+func GenerateGetMpcpLLId(oc *TomiObjectContext) gopacket.SerializableLayer {
+ tibitData := &GetMpcpLLIdReq{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OMI Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBiT OLT Management Interface
+ TOMIOpcode: 0x01,
+ // Object Context
+ OCBranch: oc.Branch,
+ OCType: oc.Type,
+ OCLength: oc.Length,
+ OCInstance: oc.Instance,
+ // Vd
+ VdBranch: 0xcc,
+ VdLeaf: 0x0007,
+ // End
+ EndBranch: 0,
+ }
+ return tibitData
+}
+
+// String returns the string expression of GetMpcpLLIdReq
+func (d *GetMpcpLLIdReq) String() string {
+ message := fmt.Sprintf("Opcode:%02x, Flags:%04x, OAMPDUCode:%02x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%02x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, OCBranch:%02x, OCType:%04x, OCLength:%02x, OCInstance:%08x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VdBranch:%02x, VdLeaf:%04x, EndBranch:%02x", message, d.VdBranch, d.VdLeaf, d.EndBranch)
+ return message
+}
+
+// Len returns the length of GetMpcpLLIdReq
+func (d *GetMpcpLLIdReq) Len() int {
+ return 21
+}
+
+// LayerType returns the ethernet type of GetMpcpLLIdReq
+func (d *GetMpcpLLIdReq) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *GetMpcpLLIdReq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Flags)
+ i += 2
+ data[i] = byte(d.OAMPDUCode)
+ i++
+ copy(data[i:i+len(d.OUId)], d.OUId)
+ i += len(d.OUId)
+ data[i] = byte(d.TOMIOpcode)
+ i++
+ data[i] = byte(d.OCBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.OCType)
+ i += 2
+ data[i] = byte(d.OCLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.OCInstance)
+ i += 4
+ data[i] = byte(d.VdBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VdLeaf)
+ i += 2
+ data[i] = byte(d.EndBranch)
+
+ return nil
+}
+
+// GetMpcpLLIdRes is a structure for a response of "MPCP/LLID" message
+type GetMpcpLLIdRes struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+ EcLength uint8
+ EcValue []byte
+ EndBranch uint8
+}
+
+// String returns the string expression of GetMpcpLLIdRes
+func (d *GetMpcpLLIdRes) String() string {
+ message := fmt.Sprintf("Opcode:%02x, Flags:%04x, OAMPDUCode:%02x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%02x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, OCBranch:%02x, OCType:%04x, OCLength:%02x, OCInstance:%08x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VcBranch:%02x, VcLeaf:%04x, VcLength:%02x", message, d.VcBranch, d.VcLeaf, d.VcLength)
+ message = fmt.Sprintf("%s, EcLength:%02x, EcValue:%v, EndBranch:%02x", message, d.EcLength, hex.EncodeToString(d.EcValue), d.EndBranch)
+ return message
+}
+
+// Len returns the length of GetMpcpLLIdRes
+func (d *GetMpcpLLIdRes) Len() int {
+ return 20 + int(d.VcLength) + 1
+}
+
+// LayerType returns the ethernet type of GetMpcpLLIdRes
+func (d *GetMpcpLLIdRes) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// Decode decodes byte arrays to a data structure
+func (d *GetMpcpLLIdRes) Decode(data []byte) error {
+ i := 0
+ d.Opcode = data[i]
+ i++
+ d.Flags = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OAMPDUCode = data[i]
+ i++
+ d.OUId = data[i : i+3]
+ i += len(d.OUId)
+ d.TOMIOpcode = data[i]
+ i++
+ d.OCBranch = data[i]
+ i++
+ d.OCType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OCLength = data[i]
+ i++
+ d.OCInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.VcBranch = data[i]
+ i++
+ d.VcLeaf = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.VcLength = data[i]
+ i++
+ d.EcLength = data[i]
+ i++
+ d.EcValue = data[i : i+int(d.EcLength)]
+ i += int(d.EcLength)
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// GetLLID returns a LLID
+func (d *GetMpcpLLIdRes) GetLLID() uint16 {
+ return binary.BigEndian.Uint16(d.EcValue)
+}
diff --git a/internal/pkg/core/l2oam/msg_get_mpcp_mac_address.go b/internal/pkg/core/l2oam/msg_get_mpcp_mac_address.go
new file mode 100644
index 0000000..7d9313c
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_get_mpcp_mac_address.go
@@ -0,0 +1,205 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "strings"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// GetMpcpMacAddressReq is a structure for "MPCP/MAC Address" message
+type GetMpcpMacAddressReq struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VdBranch uint8
+ VdLeaf uint16
+ EndBranch uint8
+}
+
+// GenerateGetMpcpMacAddress generates "MPCP/MAC Address" message
+func GenerateGetMpcpMacAddress(oc *TomiObjectContext) gopacket.SerializableLayer {
+ tibitData := &GetMpcpMacAddressReq{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OMI Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBiT OLT Management Interface
+ TOMIOpcode: 0x01,
+ // Object Context
+ OCBranch: oc.Branch,
+ OCType: oc.Type,
+ OCLength: oc.Length,
+ OCInstance: oc.Instance,
+ // Vd
+ VdBranch: 0xcc,
+ VdLeaf: 0x0008,
+ // End
+ EndBranch: 0,
+ }
+ return tibitData
+}
+
+// String returns the string expression of GetMpcpMacAddressReq
+func (d *GetMpcpMacAddressReq) String() string {
+ message := fmt.Sprintf("Opcode:%02x, Flags:%04x, OAMPDUCode:%02x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%02x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, OCBranch:%02x, OCType:%04x, OCLength:%02x, OCInstance:%08x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VdBranch:%02x, VdLeaf:%04x, EndBranch:%02x", message, d.VdBranch, d.VdLeaf, d.EndBranch)
+ return message
+}
+
+// Len returns the length of GetModuleNumberRes
+func (d *GetMpcpMacAddressReq) Len() int {
+ return 21
+}
+
+// LayerType returns the ethernet type of GetMpcpMacAddressReq
+func (d *GetMpcpMacAddressReq) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *GetMpcpMacAddressReq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Flags)
+ i += 2
+ data[i] = byte(d.OAMPDUCode)
+ i++
+ copy(data[i:i+len(d.OUId)], d.OUId)
+ i += len(d.OUId)
+ data[i] = byte(d.TOMIOpcode)
+ i++
+ data[i] = byte(d.OCBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.OCType)
+ i += 2
+ data[i] = byte(d.OCLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.OCInstance)
+ i += 4
+ data[i] = byte(d.VdBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VdLeaf)
+ i += 2
+ data[i] = byte(d.EndBranch)
+
+ return nil
+}
+
+// GetMpcpMacAddressRes is a structure for a response of "MPCP/MAC Address" message
+type GetMpcpMacAddressRes struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+ EcLength uint8
+ EcValue []byte
+ EndBranch uint8
+}
+
+// String returns the string expression of GetMpcpMacAddressRes
+func (d *GetMpcpMacAddressRes) String() string {
+ message := fmt.Sprintf("Opcode:%02x, Flags:%04x, OAMPDUCode:%02x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%02x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, OCBranch:%02x, OCType:%04x, OCLength:%02x, OCInstance:%08x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VcBranch:%02x, VcLeaf:%04x, VcLength:%02x", message, d.VcBranch, d.VcLeaf, d.VcLength)
+ message = fmt.Sprintf("%s, EcLength:%02x, EcValue:%v, EndBranch:%02x", message, d.EcLength, hex.EncodeToString(d.EcValue), d.EndBranch)
+ return message
+}
+
+// Len returns the length of GetMpcpMacAddressRes
+func (d *GetMpcpMacAddressRes) Len() int {
+ return 20 + int(d.VcLength) + 1
+}
+
+// LayerType returns the ethernet type of GetMpcpMacAddressRes
+func (d *GetMpcpMacAddressRes) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// Decode decodes byte arrays to a data structure
+func (d *GetMpcpMacAddressRes) Decode(data []byte) error {
+ i := 0
+ d.Opcode = data[i]
+ i++
+ d.Flags = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OAMPDUCode = data[i]
+ i++
+ d.OUId = data[i : i+3]
+ i += len(d.OUId)
+ d.TOMIOpcode = data[i]
+ i++
+ d.OCBranch = data[i]
+ i++
+ d.OCType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OCLength = data[i]
+ i++
+ d.OCInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.VcBranch = data[i]
+ i++
+ d.VcLeaf = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.VcLength = data[i]
+ i++
+ d.EcLength = data[i]
+ i++
+ d.EcValue = data[i : i+int(d.EcLength)]
+ i += int(d.EcLength)
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// GetMacAddress returns a MAC address
+func (d *GetMpcpMacAddressRes) GetMacAddress() string {
+ var buf []string
+ for _, b := range d.EcValue {
+ buf = append(buf, fmt.Sprintf("%02x", b))
+ }
+ return strings.Join(buf, ":")
+}
diff --git a/internal/pkg/core/l2oam/msg_get_pon_mode.go b/internal/pkg/core/l2oam/msg_get_pon_mode.go
new file mode 100644
index 0000000..74f8e00
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_get_pon_mode.go
@@ -0,0 +1,92 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+)
+
+// GenerateGetPonMode generates "Device/PON Mode" message
+func GenerateGetPonMode() gopacket.SerializableLayer {
+ tibitData := &TOAMGetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OMI Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBiT OLT Management Interface
+ TOMIOpcode: 0x01,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0dce,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vd
+ VdBranch: 0xde,
+ VdLeaf: 0x0002,
+ // End
+ EndBranch: 0,
+ }
+ return tibitData
+}
+
+// GetPonMode is a structure for toam message
+type GetPonMode struct {
+ ComResp TOAMGetResponse
+ EcLength uint8
+ EcValue []byte
+ EndBranch uint8
+}
+
+// String returns the string expression of GetPonMode
+func (d *GetPonMode) String() string {
+ message := d.ComResp.String()
+ message = fmt.Sprintf("%s, EcLength:%02x, EcValue:%s EndBranch:%02x", message, d.EcLength, hex.EncodeToString(d.EcValue), d.EndBranch)
+ return message
+}
+
+// Len returns the length of GetPonMode
+func (d *GetPonMode) Len() int {
+ return d.ComResp.Len() + int(d.ComResp.VcLength) + 1
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *GetPonMode) Decode(data []byte) error {
+ d.ComResp.Decode(data)
+ i := d.ComResp.Len()
+ d.EcLength = data[i]
+ i++
+ d.EcValue = data[i : i+int(d.EcLength)]
+ i += int(d.EcLength)
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// GetPonMode returns a PON mode
+func (d *GetPonMode) GetPonMode() string {
+ return string(d.EcValue)
+}
diff --git a/internal/pkg/core/l2oam/msg_get_serial_number.go b/internal/pkg/core/l2oam/msg_get_serial_number.go
new file mode 100644
index 0000000..73c662d
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_get_serial_number.go
@@ -0,0 +1,92 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+)
+
+// GenerateGetSerialNumber generates "Device/Vendor Serial Number" message
+func GenerateGetSerialNumber() gopacket.SerializableLayer {
+ tibitData := &TOAMGetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OMI Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBiT OLT Management Interface
+ TOMIOpcode: 0x01,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0dce,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vd
+ VdBranch: 0xde,
+ VdLeaf: 0x0012,
+ // End
+ EndBranch: 0,
+ }
+ return tibitData
+}
+
+// GetSerialNumberRes is a structure for toam message
+type GetSerialNumberRes struct {
+ ComResp TOAMGetResponse
+ EcLength uint8
+ EcValue []byte
+ EndBranch uint8
+}
+
+// String returns the string expression of GetSerialNumberRes
+func (d *GetSerialNumberRes) String() string {
+ message := d.ComResp.String()
+ message = fmt.Sprintf("%s, EcLength:%02x, EcValue:%s EndBranch:%02x", message, d.EcLength, hex.EncodeToString(d.EcValue), d.EndBranch)
+ return message
+}
+
+// Len returns the length of GetSerialNumberRes
+func (d *GetSerialNumberRes) Len() int {
+ return d.ComResp.Len() + int(d.ComResp.VcLength) + 1
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *GetSerialNumberRes) Decode(data []byte) error {
+ d.ComResp.Decode(data)
+ i := d.ComResp.Len()
+ d.EcLength = data[i]
+ i++
+ d.EcValue = data[i : i+int(d.EcLength)]
+ i += int(d.EcLength)
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// GetSerialNumber returns a serial number
+func (d *GetSerialNumberRes) GetSerialNumber() string {
+ return string(d.EcValue)
+}
diff --git a/internal/pkg/core/l2oam/msg_get_traffic_control_reference_table.go b/internal/pkg/core/l2oam/msg_get_traffic_control_reference_table.go
new file mode 100644
index 0000000..96b8c72
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_get_traffic_control_reference_table.go
@@ -0,0 +1,163 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// GenerateGetTrafficControlReferenceTableReq generates "PON Link/Traffic Control Reference Table" message
+func GenerateGetTrafficControlReferenceTableReq(oc *TomiObjectContext) gopacket.SerializableLayer {
+ data := &TOAMGetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x01,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: oc.Branch,
+ OCType: oc.Type,
+ OCLength: oc.Length,
+ OCInstance: oc.Instance,
+ // Vd
+ VdBranch: 0x01,
+ VdLeaf: 0x0007,
+ // End
+ EndBranch: 0x00,
+ }
+
+ return data
+}
+
+// GetTrafficControlReferenceTableRes is a structure for a response of "Traffic Control Reference Table" message
+type GetTrafficControlReferenceTableRes struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+
+ EcOcLengthDown uint8
+ EcOcBranchDown uint8
+ EcOcTypeDown uint16
+ EcOcLength2Down uint8
+ EcOcInstanceDown []byte
+
+ EcOcLengthUp uint8
+ EcOcBranchUp uint8
+ EcOcTypeUp uint16
+ EcOcLength2Up uint8
+ EcOcInstanceUp []byte
+
+ EndBranch uint8
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *GetTrafficControlReferenceTableRes) Decode(data []byte) error {
+ i := 0
+ d.Opcode = data[i]
+ i++
+ d.Flags = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OAMPDUCode = data[i]
+ i++
+ d.OUId = data[i : i+3]
+ i += len(d.OUId)
+ d.TOMIOpcode = data[i]
+ i++
+ d.CTBranch = data[i]
+ i++
+ d.CTType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.CTLength = data[i]
+ i++
+ d.CTInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.OCBranch = data[i]
+ i++
+ d.OCType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OCLength = data[i]
+ i++
+ d.OCInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.VcBranch = data[i]
+ i++
+ d.VcLeaf = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.VcLength = data[i]
+ i++
+
+ d.EcOcLengthDown = data[i]
+ i++
+ d.EcOcBranchDown = data[i]
+ i++
+ d.EcOcTypeDown = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.EcOcLength2Down = data[i]
+ i++
+ d.EcOcInstanceDown = data[i : i+int(d.EcOcLength2Down)]
+ i += int(d.EcOcLength2Down)
+
+ d.EcOcLengthUp = data[i]
+ i++
+ d.EcOcBranchUp = data[i]
+ i++
+ d.EcOcTypeUp = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.EcOcLength2Up = data[i]
+ i++
+ d.EcOcInstanceUp = data[i : i+int(d.EcOcLength2Up)]
+ i += int(d.EcOcLength2Up)
+
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// GetReferenceControlDown returns a link id for downstream
+func (d *GetTrafficControlReferenceTableRes) GetReferenceControlDown() []byte {
+ return d.EcOcInstanceDown
+}
+
+// GetReferenceControlUp returns a link id for upstream
+func (d *GetTrafficControlReferenceTableRes) GetReferenceControlUp() []byte {
+ return d.EcOcInstanceUp
+}
diff --git a/internal/pkg/core/l2oam/msg_get_vendor_name.go b/internal/pkg/core/l2oam/msg_get_vendor_name.go
new file mode 100644
index 0000000..aa80fcd
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_get_vendor_name.go
@@ -0,0 +1,93 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+)
+
+// GnerateGetVendorName generates "Device/Vendor Name"
+func GnerateGetVendorName() gopacket.SerializableLayer {
+ tibitData := &TOAMGetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x01,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0dce,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vd
+ VdBranch: 0xde,
+ VdLeaf: 0x0009,
+ // End
+ EndBranch: 0x00,
+ }
+
+ return tibitData
+}
+
+// GetVendorNameRes is a structure for a response of "Device/Vendor Name" message
+type GetVendorNameRes struct {
+ ComResp TOAMGetResponse
+ EcLength uint8
+ EcValue []byte
+ EndBranch uint8
+}
+
+// String returns the string expression of GetVendorNameRes
+func (d *GetVendorNameRes) String() string {
+ message := d.ComResp.String()
+ message = fmt.Sprintf("%s, EcLength:%v, EcValue:%v, EndBranch:%v", message, d.EcLength, hex.EncodeToString(d.EcValue), d.EndBranch)
+ return message
+}
+
+// Len returns the length of GetVendorNameRes
+func (d *GetVendorNameRes) Len() int {
+ return d.ComResp.Len() + int(d.ComResp.VcLength) + 1
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *GetVendorNameRes) Decode(data []byte) error {
+ d.ComResp.Decode(data)
+ i := d.ComResp.Len()
+ d.EcLength = data[i]
+ i++
+ d.EcValue = data[i : i+int(d.EcLength)]
+ i = i + int(d.EcLength)
+ d.EndBranch = data[i]
+
+ return nil
+}
+
+// GetVendorName returns a vendor name
+func (d *GetVendorNameRes) GetVendorName() string {
+ return string(d.EcValue)
+}
diff --git a/internal/pkg/core/l2oam/msg_guaranteed_mbs.go b/internal/pkg/core/l2oam/msg_guaranteed_mbs.go
new file mode 100644
index 0000000..675e44e
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_guaranteed_mbs.go
@@ -0,0 +1,56 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateGuaranteedMbs generates "Traffic Profile/Guaranteed MBS" message
+func GenerateGuaranteedMbs() gopacket.SerializableLayer {
+
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x070f,
+ OCLength: 4,
+ OCInstance: 0x00000002,
+ // Vc
+ VcBranch: 0x7f,
+ VcLeaf: 0x0007,
+ VcLength: 5,
+ // EC
+ ECLength: 4,
+ ECValue: []byte{0x00, 0x06, 0x40, 0x00},
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_guaranteed_rate.go b/internal/pkg/core/l2oam/msg_guaranteed_rate.go
new file mode 100644
index 0000000..c26825d
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_guaranteed_rate.go
@@ -0,0 +1,58 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+
+ "github.com/google/gopacket"
+)
+
+// GenerateGuaranteedRate generates "Traffic Profile/Guaranteed Rate" message
+func GenerateGuaranteedRate(ecvalue []byte, trafficProfile []byte) gopacket.SerializableLayer {
+
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x070f,
+ OCLength: 4,
+ OCInstance: binary.BigEndian.Uint32(trafficProfile),
+ // Vc
+ VcBranch: 0x7f,
+ VcLeaf: 0x0006,
+ VcLength: uint8(1 + len(ecvalue)), //
+ // EC
+ ECLength: uint8(len(ecvalue)), //
+ ECValue: ecvalue, //0x80
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_l2switching_domain_action_Inlet_entry_del.go b/internal/pkg/core/l2oam/msg_l2switching_domain_action_Inlet_entry_del.go
new file mode 100644
index 0000000..667fb7f
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_l2switching_domain_action_Inlet_entry_del.go
@@ -0,0 +1,184 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateL2switchingDomainActionInletEntryUsDel generates "L2 Switching Domain/Action Delete Inlet entry(for upstream)"
+func GenerateL2switchingDomainActionInletEntryUsDel(oc *TomiObjectContext,
+ tpid []byte, vid []byte, onuID *TomiObjectContext) gopacket.SerializableLayer {
+ tibitData := &L2switchingDomainRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: oc.Branch,
+ OCType: oc.Type,
+ OCLength: oc.Length,
+ OCInstance: oc.Instance,
+ // Vc
+ VcBranch: 0x5d,
+ VcLeaf: 0x7002,
+ VcLength: 21 + uint8(1+1+len(tpid)+1+len(vid)),
+ // Source OC
+ SOLength: 5,
+ SOBranch: 0x0c,
+ SOType: 0x0011,
+ SOValLength: 1,
+ SOInstance: uint8(onuID.Instance),
+ // TagMatchList
+ TMLLength: 7,
+ TMLList: []TpidVid{
+ {
+ Length: 3,
+ TpIDLength: 0x80,
+ TpIDValue: []byte{},
+ VIdLength: 1,
+ VIdValue: []byte{0x00},
+ },
+ {
+ Length: 2,
+ TpIDLength: 0x80,
+ TpIDValue: []byte{},
+ VIdLength: 0x80,
+ VIdValue: []byte{},
+ },
+ },
+ // TagOpPop(UI)
+ TOPopLength: 1,
+ TOPopValue: 0x00,
+ // TagOpSetList
+ TOSLength: 3,
+ TOSList: []TpidVid{
+ {
+ Length: 2,
+ TpIDLength: 0x80,
+ TpIDValue: []byte{},
+ VIdLength: 0x80,
+ VIdValue: []byte{},
+ },
+ },
+ // TagOpPushList
+ TOPushLength: uint8(1 + 1 + len(tpid) + 1 + len(vid)),
+ TOPushList: []TpidVid{
+ {
+ Length: uint8(1 + len(tpid) + 1 + len(vid)),
+ TpIDLength: uint8(len(tpid)),
+ TpIDValue: tpid,
+ VIdLength: uint8(len(vid)),
+ VIdValue: vid,
+ },
+ },
+ // End
+ EndBranch: 0x00,
+ }
+
+ return tibitData
+}
+
+// GenerateL2switchingDomainActionInletEntryDsDel generates "L2 Switching Domain/Action Delete Inlet entry(for downstream)"
+func GenerateL2switchingDomainActionInletEntryDsDel(oc *TomiObjectContext, tpid []byte, vid []byte) gopacket.SerializableLayer {
+ tibitData := &L2switchingDomainRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: oc.Branch,
+ OCType: oc.Type,
+ OCLength: oc.Length,
+ OCInstance: oc.Instance,
+ // Vc
+ VcBranch: 0x5d,
+ VcLeaf: 0x7002,
+ VcLength: 16 + uint8(1+1+len(tpid)+1+len(vid)+4),
+ // Source OC
+ SOLength: 5,
+ SOBranch: 0x0c,
+ SOType: 0x0eca,
+ SOValLength: 1,
+ SOInstance: 0x00,
+ // TagMatchList
+ TMLLength: uint8(1 + 1 + len(tpid) + 1 + len(vid) + 3),
+ TMLList: []TpidVid{
+ {
+ Length: uint8(1 + len(tpid) + 1 + len(vid)),
+ TpIDLength: uint8(len(tpid)),
+ TpIDValue: tpid,
+ VIdLength: uint8(len(vid)),
+ VIdValue: vid,
+ },
+ {
+ Length: 2,
+ TpIDLength: 0x80,
+ TpIDValue: []byte{},
+ VIdLength: 0x80,
+ VIdValue: []byte{},
+ },
+ },
+ // TagOpPop(UI)
+ TOPopLength: 1,
+ TOPopValue: 0x01,
+ // TagOpSetList
+ TOSLength: 3,
+ TOSList: []TpidVid{
+ {
+ Length: 2,
+ TpIDLength: 0x80,
+ TpIDValue: []byte{},
+ VIdLength: 0x80,
+ VIdValue: []byte{},
+ },
+ },
+ // TagOpPushList
+ TOPushLength: 3,
+ TOPushList: []TpidVid{
+ {
+ Length: 2,
+ TpIDLength: 0x80,
+ TpIDValue: []byte{},
+ VIdLength: 0x80,
+ VIdValue: []byte{},
+ },
+ },
+ // End
+ EndBranch: 0x00,
+ }
+
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_l2switching_domain_action_lnlet_entry.go b/internal/pkg/core/l2oam/msg_l2switching_domain_action_lnlet_entry.go
new file mode 100644
index 0000000..b826292
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_l2switching_domain_action_lnlet_entry.go
@@ -0,0 +1,215 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// L2switchingDomainRequest is a structure for a request of "L2 Switching Domain/Action Inlet entry" message
+type L2switchingDomainRequest struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+ SOLength uint8
+ SOBranch uint8
+ SOType uint16
+ SOValLength uint8
+ SOInstance uint8
+ TMLLength uint8
+ TMLList []TpidVid
+ TOPopLength uint8
+ TOPopValue uint8
+ TOSLength uint8
+ TOSList []TpidVid
+ TOPushLength uint8
+ TOPushList []TpidVid
+ EndBranch uint8
+ SizeMargin uint8
+}
+
+// TpidVid is a structure for TPID/VID set
+type TpidVid struct {
+ Length uint8
+ TpIDLength uint8
+ TpIDValue []byte
+ VIdLength uint8
+ VIdValue []byte
+}
+
+// String returns the string expression of L2switchingDomainRequest
+func (d *L2switchingDomainRequest) String() string {
+ message := fmt.Sprintf("Opcode:%x, Flags:%x, OAMPDUCode:%x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, CTBranch:%x, CTType:%x, CTLength:%x, CTInstance:%x", message, d.CTBranch, d.CTType, d.CTLength, d.CTInstance)
+ message = fmt.Sprintf("%s, OCBranch:%x, OCType:%x, OCLength:%x, OCInstance:%x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VcBranch:%x, VcLeaf:%x, VcLength:%x", message, d.VcBranch, d.VcLeaf, d.VcLength)
+ message = fmt.Sprintf("%s, SOLength:%x, SOBranch:%x, SOType:%x, SOValLength:%x, SOInstance:%x", message, d.SOLength, d.SOBranch, d.SOType, d.SOValLength, d.SOInstance)
+ message = fmt.Sprintf("%s, TMLLength:%x", message, d.TMLLength)
+ for i, tagMatch := range d.TMLList {
+ message = fmt.Sprintf("%s, %s", message, stringToTpidVid(&tagMatch, i))
+ }
+ message = fmt.Sprintf("%s, TOPopLength:%x, TOPopValue:%x", message, d.TOPopLength, d.TOPopValue)
+ message = fmt.Sprintf("%s, TOSLength:%x", message, d.TOSLength)
+ for i, tagOpSet := range d.TOSList {
+ message = fmt.Sprintf("%s, %s", message, stringToTpidVid(&tagOpSet, i))
+ }
+ message = fmt.Sprintf("%s, TOPushLength:%x", message, d.TOPushLength)
+ for i, tagOpPush := range d.TOPushList {
+ message = fmt.Sprintf("%s, %s", message, stringToTpidVid(&tagOpPush, i))
+ }
+ return message
+}
+
+func stringToTpidVid(tpidVid *TpidVid, index int) string {
+ message := fmt.Sprintf("EC[%v]:{Length:%x, TpIdLength:%x", index, tpidVid.Length, tpidVid.TpIDLength)
+ if tpidVid.TpIDLength != 128 {
+ message = fmt.Sprintf("%s, TpIdValue:%v", message, hex.EncodeToString(tpidVid.TpIDValue))
+ }
+ message = fmt.Sprintf("%s, VIdLength:%x", message, tpidVid.VIdLength)
+ if tpidVid.VIdLength != 128 {
+ message = fmt.Sprintf("%s, VIdValue:%v", message, hex.EncodeToString(tpidVid.VIdValue))
+ }
+ message = fmt.Sprintf("%s}", message)
+ return message
+}
+
+// Len returns the length of L2switchingDomainRequest
+func (d *L2switchingDomainRequest) Len() int {
+ return 22 + int(d.CTLength) + int(d.OCLength) + int(d.VcLength) + int(d.SizeMargin)
+}
+
+// LayerType returns the ethernet type of L2switchingDomainRequest
+func (d *L2switchingDomainRequest) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *L2switchingDomainRequest) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Flags)
+ i += 2
+ data[i] = byte(d.OAMPDUCode)
+ i++
+ copy(data[i:i+len(d.OUId)], d.OUId)
+ i += len(d.OUId)
+ data[i] = byte(d.TOMIOpcode)
+ i++
+ data[i] = byte(d.CTBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.CTType)
+ i += 2
+ data[i] = byte(d.CTLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.CTInstance)
+ i += 4
+ data[i] = byte(d.OCBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.OCType)
+ i += 2
+ data[i] = byte(d.OCLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.OCInstance)
+ i += 4
+ data[i] = byte(d.VcBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VcLeaf)
+ i += 2
+ data[i] = byte(d.VcLength)
+ i++
+ data[i] = byte(d.SOLength)
+ i++
+ data[i] = byte(d.SOBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.SOType)
+ i += 2
+ data[i] = byte(d.SOValLength)
+ i++
+ data[i] = byte(d.SOInstance)
+ i++
+ data[i] = byte(d.TMLLength)
+ i++
+ for _, tagMatch := range d.TMLList {
+ nextIndex := serializeTpidVid(&tagMatch, data, i)
+ i = nextIndex
+ }
+ data[i] = byte(d.TOPopLength)
+ i++
+ data[i] = byte(d.TOPopValue)
+ i++
+ data[i] = byte(d.TOSLength)
+ i++
+ for _, tagOpSet := range d.TOSList {
+ nextIndex := serializeTpidVid(&tagOpSet, data, i)
+ i = nextIndex
+ }
+ data[i] = d.TOPushLength
+ i++
+ for _, tagOpPush := range d.TOPushList {
+ nextIndex := serializeTpidVid(&tagOpPush, data, i)
+ i = nextIndex
+ }
+ data[i] = byte(d.EndBranch)
+
+ return nil
+}
+
+func serializeTpidVid(tpidVid *TpidVid, data []byte, startIndex int) int {
+ i := startIndex
+ data[i] = tpidVid.Length
+ i++
+ ln := tpidVid.TpIDLength
+ data[i] = ln
+ i++
+ if ln != 128 { // !Empty?
+ copy(data[i:i+int(ln)], tpidVid.TpIDValue)
+ i += int(ln)
+ }
+ ln = tpidVid.VIdLength
+ data[i] = ln
+ i++
+ if ln != 128 { // !Empty?
+ copy(data[i:i+int(ln)], tpidVid.VIdValue)
+ i += int(ln)
+ }
+ return i
+}
diff --git a/internal/pkg/core/l2oam/msg_l2switching_domain_action_lnlet_entry_ds.go b/internal/pkg/core/l2oam/msg_l2switching_domain_action_lnlet_entry_ds.go
new file mode 100644
index 0000000..6013ce3
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_l2switching_domain_action_lnlet_entry_ds.go
@@ -0,0 +1,85 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateL2switchingDomainActionLnletEntryDs generates "L2 Switching Domain/Action Add Inlet entry(for downstream)"
+func GenerateL2switchingDomainActionLnletEntryDs(oc *TomiObjectContext, tmTpidValue []byte, tmVidValue []byte) gopacket.SerializableLayer {
+
+ tibitData := &L2switchingDomainRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: oc.Branch,
+ OCType: oc.Type,
+ OCLength: oc.Length,
+ OCInstance: oc.Instance,
+ // Vc
+ VcBranch: 0x5d,
+ VcLeaf: 0x7001,
+ //VcLength: uint8(24 + len(tmTpidValue) + len(tmVidValue)), //27
+ VcLength: uint8(23 + len(tmTpidValue) + len(tmVidValue)), //26
+ // Source OC
+ SOLength: 5,
+ SOBranch: 0x0c,
+ SOType: 0x0eca,
+ SOValLength: 1,
+ SOInstance: 0x00,
+ // TagMatchList
+ //TMLLength: uint8(7 + len(tmTpidValue) + len(tmVidValue)), //10
+ TMLLength: uint8(6 + len(tmTpidValue) + len(tmVidValue)), //9
+ TMLList: []TpidVid{
+ {Length: uint8(2 + len(tmTpidValue) + len(tmVidValue)),
+ TpIDLength: uint8(len(tmTpidValue)),
+ TpIDValue: tmTpidValue,
+ VIdLength: uint8(len(tmVidValue)),
+ VIdValue: tmVidValue},
+ ////{Length: 5, TpIdLength: 2, TpIdValue: []byte{0x88, 0xa8}, VIdLength: 1, VIdValue: []byte{0x64}}
+
+ //{Length: 3, TpIdLength: 128, TpIdValue: []byte{0}, VIdLength: 1, VIdValue: []byte{0x00}}},
+ {Length: 2, TpIDLength: 128, TpIDValue: []byte{0}, VIdLength: 128, VIdValue: []byte{0x00}}},
+ // TagOpPop(UI)
+ TOPopLength: 1,
+ TOPopValue: 0x01,
+ // TagOpSetList
+ TOSLength: 3,
+ TOSList: []TpidVid{
+ {Length: 2, TpIDLength: 128, TpIDValue: []byte{0}, VIdLength: 128, VIdValue: []byte{0}}},
+ // TagOpPushList
+ TOPushLength: 3,
+ TOPushList: []TpidVid{
+ {Length: 2, TpIDLength: 128, TpIDValue: []byte{0}, VIdLength: 128, VIdValue: []byte{0}}},
+ // End
+ EndBranch: 0x00,
+ }
+
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_l2switching_domain_action_lnlet_entry_us.go b/internal/pkg/core/l2oam/msg_l2switching_domain_action_lnlet_entry_us.go
new file mode 100644
index 0000000..5f07c98
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_l2switching_domain_action_lnlet_entry_us.go
@@ -0,0 +1,99 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateL2switchingDomainActionLnletEntryUs generates "L2 Switching Domain/Action Add Inlet entry(upstream)" message
+func GenerateL2switchingDomainActionLnletEntryUs(oc *TomiObjectContext,
+ pushTpidValue []byte, pushVidValue []byte, onuID *TomiObjectContext) gopacket.SerializableLayer {
+
+ tibitData := &L2switchingDomainRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: oc.Branch,
+ OCType: oc.Type,
+ OCLength: oc.Length,
+ OCInstance: oc.Instance,
+ // Vc
+ VcBranch: 0x5d,
+ VcLeaf: 0x7001,
+ VcLength: uint8(24 + len(pushTpidValue) + len(pushVidValue)), //27
+ // Source OC
+ SOLength: 5,
+ SOBranch: 0x0c,
+ SOType: 0x0011,
+ SOValLength: 1,
+ SOInstance: uint8(onuID.Instance),
+ // TagMatchList
+ TMLLength: 7,
+ TMLList: []TpidVid{
+ {
+ Length: 3,
+ TpIDLength: 0x80,
+ TpIDValue: []byte{0},
+ VIdLength: 1,
+ VIdValue: []byte{0x00},
+ },
+ {
+ Length: 2,
+ TpIDLength: 0x80,
+ TpIDValue: []byte{0},
+ VIdLength: 0x80,
+ VIdValue: []byte{0},
+ }},
+ // TagOpPop(UI)
+ TOPopLength: 1,
+ TOPopValue: 0x00,
+ // TagOpSetList
+ TOSLength: 3,
+ TOSList: []TpidVid{
+ {
+ Length: 2,
+ TpIDLength: 0x80,
+ TpIDValue: []byte{0},
+ VIdLength: 0x80,
+ VIdValue: []byte{0},
+ }},
+ // TagOpPushList
+ TOPushLength: uint8(3 + len(pushTpidValue) + len(pushVidValue)), //6
+ TOPushList: []TpidVid{
+ {Length: uint8(2 + len(pushTpidValue) + len(pushVidValue)),
+ TpIDLength: uint8(len(pushTpidValue)),
+ TpIDValue: pushTpidValue,
+ VIdLength: uint8(len(pushVidValue)),
+ VIdValue: pushVidValue}}, ///{Length: 5, TpIdLength: 2, TpIdValue: []byte{0x88, 0xa8}, VIdLength: 1, VIdValue: []byte{0x64}}
+ // End
+ EndBranch: 0x00,
+ }
+
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_oampdu_information.go b/internal/pkg/core/l2oam/msg_oampdu_information.go
new file mode 100644
index 0000000..6d7a9f8
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_oampdu_information.go
@@ -0,0 +1,263 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// OampduTlvTypeLocalInfo means that TLV type is "Local Information"
+var OampduTlvTypeLocalInfo uint8 = 0x01
+
+// OampduTlvTypeRemoteInfo means that TLV type is "Remote Information"
+var OampduTlvTypeRemoteInfo uint8 = 0x02
+
+// OampduTlvTypeOrganizationSpecificInfo means that TLV type is "Organization Specific Information"
+var OampduTlvTypeOrganizationSpecificInfo uint8 = 0xfe
+
+// GeneateKeepAlive1 generates "OAMPDU Information(first)"
+func GeneateKeepAlive1(isOnuPkgA bool) gopacket.SerializableLayer {
+ var osiLength uint8
+ var osiValue []byte
+ if isOnuPkgA {
+ osiLength = 7
+ osiValue = []byte{0x00, 0x10, 0x00, 0x00, 0x23}
+ } else {
+ osiLength = 8
+ osiValue = []byte{0x90, 0x82, 0x60, 0x02, 0x01, 0x01}
+ }
+ tibitData := &OAMPDUInformation{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0008,
+ Code: 0x00,
+ // Local Information TLV
+ LIType: OampduTlvTypeLocalInfo,
+ LILength: 16,
+ LIValue: []byte{0x01, 0x00, 0x00, 0x00, 0x1b, 0x04, 0xb0, 0x2a, 0xea, 0x15, 0x00, 0x00, 0x00, 0x23},
+ // Organization Specific Information TLV
+ OSIType: OampduTlvTypeOrganizationSpecificInfo,
+ OSILength: osiLength,
+ OSIValue: osiValue,
+ }
+
+ return tibitData
+}
+
+// GeneateKeepAlive2 generates "OAMPDU Information(second)"
+func GeneateKeepAlive2(riValue []byte, isOnuPkgA bool) gopacket.SerializableLayer {
+ var osiLength uint8
+ var osiValue []byte
+ if isOnuPkgA {
+ osiLength = 7
+ osiValue = []byte{0x00, 0x10, 0x00, 0x00, 0x23}
+ } else {
+ osiLength = 8
+ osiValue = []byte{0x90, 0x82, 0x60, 0x02, 0x01, 0x01}
+ }
+ tibitData := &OAMPDUInformation{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0030,
+ Code: 0x00,
+ // Local Information TLV
+ LIType: OampduTlvTypeLocalInfo,
+ LILength: 16,
+ LIValue: []byte{0x01, 0x00, 0x00, 0x00, 0x1b, 0x04, 0xb0, 0x2a, 0xea, 0x15, 0x00, 0x00, 0x00, 0x23},
+ // Remote Information TLV
+ RIType: OampduTlvTypeRemoteInfo,
+ RILength: 16,
+ RIValue: riValue,
+ // Organization Specific Information TLV
+ OSIType: OampduTlvTypeOrganizationSpecificInfo,
+ OSILength: osiLength,
+ OSIValue: osiValue,
+ }
+
+ return tibitData
+
+}
+
+// GeneateKeepAlive3 generates "OAMPDU Information(third)"
+func GeneateKeepAlive3(riValue []byte) gopacket.SerializableLayer {
+ tibitData := &OAMPDUInformation{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ Code: 0x00,
+ // Local Information TLV
+ LIType: OampduTlvTypeLocalInfo,
+ LILength: 16,
+ LIValue: []byte{0x01, 0x00, 0x00, 0x00, 0x1b, 0x04, 0xb0, 0x2a, 0xea, 0x15, 0x00, 0x00, 0x00, 0x23},
+ // Remote Information TLV
+ RIType: OampduTlvTypeRemoteInfo,
+ RILength: 16,
+ RIValue: riValue,
+ }
+
+ return tibitData
+
+}
+
+// OAMPDUInformation is a structure for "OAMPDU Information" message
+type OAMPDUInformation struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ Code uint8
+ LIType uint8
+ LILength uint8
+ LIValue []byte
+ RIType uint8
+ RILength uint8
+ RIValue []byte
+ OSIType uint8
+ OSILength uint8
+ OSIValue []byte
+}
+
+// String returns the string expression of OAMPDUInformation
+func (d *OAMPDUInformation) String() string {
+ message := fmt.Sprintf("Opcode:%v, Flags:%v, Code:%v", d.Opcode, d.Flags, d.Code)
+ message = fmt.Sprintf("%s, LIType:%v, LILength:%v, LIValue:%v", message, d.LIType, d.LILength, hex.EncodeToString(d.LIValue))
+ message = fmt.Sprintf("%s, RIType:%v, RILength:%v, RIValue:%v", message, d.RIType, d.RILength, hex.EncodeToString(d.RIValue))
+ message = fmt.Sprintf("%s, OSIType:%v, OSILength:%v, OSIValue:%v", message, d.OSIType, d.OSILength, hex.EncodeToString(d.OSIValue))
+ return message
+}
+
+// Len returns the length of OAMPDUInformation
+func (d *OAMPDUInformation) Len() int {
+ len := (1) + (3) + int(d.LILength) + int(d.RILength) + int(d.OSILength)
+ return len
+}
+
+// LayerType returns the ethernet type of OAMPDUInformation
+func (d *OAMPDUInformation) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *OAMPDUInformation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Flags)
+ i = i + 2
+ data[i] = byte(d.Code)
+ i++
+
+ if d.LILength != 0 {
+ data[i] = byte(d.LIType)
+ data[i+1] = byte(d.LILength)
+ copy(data[i+2:i+int(d.LILength)], d.LIValue)
+ i = i + int(d.LILength)
+ }
+
+ if d.RILength != 0 {
+ data[i] = byte(d.RIType)
+ data[i+1] = byte(d.RILength)
+ copy(data[i+2:i+int(d.RILength)], d.RIValue)
+ i = i + int(d.RILength)
+ }
+
+ if d.OSILength != 0 {
+ data[i] = byte(d.OSIType)
+ data[i+1] = byte(d.OSILength)
+ copy(data[i+2:i+int(d.OSILength)], d.OSIValue)
+ //i = i + int(d.OSILength)
+ }
+
+ return nil
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *OAMPDUInformation) Decode(data []byte) error {
+ i := 0
+ d.Opcode = data[i]
+ i++
+
+ d.Flags = binary.BigEndian.Uint16(data[i : i+2])
+ i = i + 2
+ d.Code = data[i]
+ i++
+
+ for {
+ if len(data) <= i {
+ break
+ }
+ tlvType := data[i]
+ tlvLength := data[i+1]
+ if tlvLength == 0 {
+ break
+ }
+ tlvValue := data[i+2 : i+int(tlvLength)]
+ i = i + int(tlvLength)
+
+ switch tlvType {
+ case OampduTlvTypeLocalInfo:
+ d.LIType = tlvType
+ d.LILength = tlvLength
+ d.LIValue = tlvValue
+ case OampduTlvTypeRemoteInfo:
+ d.RIType = tlvType
+ d.RILength = tlvLength
+ d.RIValue = tlvValue
+ case OampduTlvTypeOrganizationSpecificInfo:
+ d.OSIType = tlvType
+ d.OSILength = tlvLength
+ d.OSIValue = tlvValue
+ default:
+ return fmt.Errorf("tlvType Error: %v", tlvType)
+ }
+ }
+
+ return nil
+}
+
+// IsOnuPkgA returns true if message type of OAMPDUInformation is PkgA
+func (d *OAMPDUInformation) IsOnuPkgA() bool {
+ if d.OSILength == 0 {
+ // return true if OSILength is 0, this means the message is KeepAlive3
+ return true
+ } else if d.OSILength == 7 {
+ return (d.OSIValue[0] == 0x00 && d.OSIValue[1] == 0x10 && d.OSIValue[2] == 0x00)
+ }
+ return false
+}
+
+// IsOnuPkgB returns true if message type of OAMPDUInformation is PkgA
+func (d *OAMPDUInformation) IsOnuPkgB() bool {
+ if d.OSILength == 0 {
+ // return true if OSILength is 0, this means the message is KeepAlive3
+ return true
+ } else if d.OSILength == 7 {
+ return (d.OSIValue[0] == 0x90 && d.OSIValue[1] == 0x82 && d.OSIValue[2] == 0x60)
+ }
+ return false
+}
diff --git a/internal/pkg/core/l2oam/msg_pcscompu_tibitcom.go b/internal/pkg/core/l2oam/msg_pcscompu_tibitcom.go
new file mode 100644
index 0000000..80e7d18
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_pcscompu_tibitcom.go
@@ -0,0 +1,103 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+)
+
+func GeneratePcscompuTivitcom(oc *TomiObjectContext) gopacket.SerializableLayer {
+ tibitData := &TOAMGetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OMI Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBiT OLT Management Interface
+ TOMIOpcode: 0x81,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: 0x00000003,
+ // Object Context
+ OCBranch: oc.Branch,
+ OCType: oc.Type,
+ OCLength: oc.Length,
+ OCInstance: oc.Instance,
+ // Vd
+ VdBranch: 0x01,
+ VdLeaf: 0x0007,
+ // End
+ EndBranch: 0,
+ }
+
+ return tibitData
+}
+
+type GetTrafficControlRefTableRes struct {
+ ComResp TOAMGetResponse
+
+ ECOC []EcOcPONLinkTrafficControlReferenceTable
+ EndBranch uint8
+}
+
+type EcOcPONLinkTrafficControlReferenceTable struct {
+ EcOcLength uint8
+ EcOcBranch uint8
+ EcOcType uint16
+ EcOcLength2 uint8
+ EcOcInstance []byte
+}
+
+func (d *GetTrafficControlRefTableRes) String() string {
+ message := d.ComResp.String()
+ for k, ec := range d.ECOC {
+ message = message + "EC[" + string(k) + "],EcOcLength:" + string(ec.EcOcLength) + ",EcOcBranch:" + string(ec.EcOcBranch) + ",EcOcType:" + string(ec.EcOcType) + ",EcOcLength2" + string(ec.EcOcLength2) + ",EcOcInstance:" + hex.EncodeToString(ec.EcOcInstance)
+ }
+ message = fmt.Sprintf("%s", message)
+ return message
+
+}
+
+func (d *GetTrafficControlRefTableRes) Decode(data []byte) error {
+ d.ComResp.Decode(data)
+ i := d.ComResp.Len()
+ for _, ec := range d.ECOC {
+ ec.EcOcLength = data[i]
+ i++
+ ec.EcOcBranch = data[i]
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], ec.EcOcType)
+ i = i + 2
+ ec.EcOcLength2 = data[i]
+ i++
+ copy(data[i:i+int(ec.EcOcLength2)], ec.EcOcInstance)
+ i = i + int(ec.EcOcLength2)
+ }
+
+ d.EndBranch = data[i]
+ i++
+
+ return nil
+
+}
diff --git a/internal/pkg/core/l2oam/msg_priority.go b/internal/pkg/core/l2oam/msg_priority.go
new file mode 100644
index 0000000..cbfff24
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_priority.go
@@ -0,0 +1,58 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+
+ "github.com/google/gopacket"
+)
+
+// GeneratePriority generates "Traffic Profile/Priority" message
+func GeneratePriority(trafficProfile []byte) gopacket.SerializableLayer {
+
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x070f,
+ OCLength: 4,
+ OCInstance: binary.BigEndian.Uint32(trafficProfile),
+ // Vc
+ VcBranch: 0x7f,
+ VcLeaf: 0x000a,
+ VcLength: 2,
+ // EC
+ ECLength: 1,
+ ECValue: []byte{0x04},
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_set_action_delete.go b/internal/pkg/core/l2oam/msg_set_action_delete.go
new file mode 100644
index 0000000..b05f661
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_action_delete.go
@@ -0,0 +1,93 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+
+ "github.com/google/gopacket"
+)
+
+// GenerateSetActionDelete generates "Generic/Action Delete" message
+func GenerateSetActionDelete(trafficProfile []byte, actionType int) gopacket.SerializableLayer {
+ ocInstance := binary.BigEndian.Uint32(trafficProfile)
+ objectType := getObjectType(actionType)
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: objectType[0],
+ OCType: binary.BigEndian.Uint16(objectType[1:3]),
+ OCLength: 4,
+ OCInstance: ocInstance,
+ // Vc
+ VcBranch: 0x6e,
+ VcLeaf: 0x7002,
+ VcLength: 0x80,
+ // EC
+ ECLength: 0,
+ ECValue: []byte{},
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
+
+// GenerateSetActionDeleteStream generates "Generic/Action Delete(for stream)" message
+func GenerateSetActionDeleteStream(oc *TomiObjectContext) gopacket.SerializableLayer {
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: oc.Branch,
+ OCType: oc.Type,
+ OCLength: oc.Length,
+ OCInstance: oc.Instance,
+ // Vc
+ VcBranch: 0x6e,
+ VcLeaf: 0x7002,
+ VcLength: 0x80,
+ // EC
+ ECLength: 0,
+ ECValue: []byte{},
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_set_action_delete_onu.go b/internal/pkg/core/l2oam/msg_set_action_delete_onu.go
new file mode 100644
index 0000000..fe8a728
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_action_delete_onu.go
@@ -0,0 +1,55 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetActionDeleteOnu generates "Generic/Action Delete" message
+func GenerateSetActionDeleteOnu(oc *TomiObjectContext) gopacket.SerializableLayer {
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: oc.Branch,
+ OCType: oc.Type,
+ OCLength: oc.Length,
+ OCInstance: oc.Instance,
+ // Vc
+ VcBranch: 0x6e,
+ VcLeaf: 0x7002,
+ VcLength: 0x80,
+ // EC
+ ECLength: 0,
+ ECValue: []byte{},
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_set_action_reset.go b/internal/pkg/core/l2oam/msg_set_action_reset.go
new file mode 100644
index 0000000..0dc4a28
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_action_reset.go
@@ -0,0 +1,40 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+
+ "github.com/google/gopacket"
+)
+
+// GenerateSetActionReset generates "Device/Action Reset" message
+func GenerateSetActionReset() gopacket.SerializableLayer {
+
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x2a, 0xea, 0x15, 0x03,
+ 0x0c, 0x0c, 0x7a, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x0d, 0xce, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0xde, 0x70, 0x01, 0x80, 0x00, 0x00},
+ }
+
+ binary.BigEndian.PutUint32(data.Data[12:16], getOltInstance())
+
+ return data
+
+}
diff --git a/internal/pkg/core/l2oam/msg_set_admin_state.go b/internal/pkg/core/l2oam/msg_set_admin_state.go
new file mode 100644
index 0000000..aae58ab
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_admin_state.go
@@ -0,0 +1,91 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetAdminState generates "PON Port/Admin State" message
+func GenerateSetAdminState() gopacket.SerializableLayer {
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0007,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vc
+ VcBranch: 0x07,
+ VcLeaf: 0x0001,
+ VcLength: 2,
+ // EC
+ ECLength: 1,
+ ECValue: []byte{0x01},
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+
+}
+
+// GenerateSetAdminStateDelete generates "PON Port/Admin State(for delete)" message
+func GenerateSetAdminStateDelete() gopacket.SerializableLayer {
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0007,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vc
+ VcBranch: 0x07,
+ VcLeaf: 0x0001,
+ VcLength: 2,
+ // EC
+ ECLength: 1,
+ ECValue: []byte{0x02},
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+
+}
diff --git a/internal/pkg/core/l2oam/msg_set_capture_protocols.go b/internal/pkg/core/l2oam/msg_set_capture_protocols.go
new file mode 100644
index 0000000..49c335a
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_capture_protocols.go
@@ -0,0 +1,208 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// GenerateCaptureProtocols generates "Protocol Filter/Capture Protocols" message
+func GenerateCaptureProtocols(ocInstance uint32) gopacket.SerializableLayer {
+
+ return &SetGenerteCaptureProtocolsreq{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0cff,
+ OCLength: 4,
+ OCInstance: ocInstance,
+ // Vc
+ VcBranch: 0xcf,
+ VcLeaf: 0x0003,
+ VcLength: 5,
+ // EC
+ ECLength: 4,
+ //Protocol
+ ProtocolLength: 1,
+ ProtocolValue: 0x01,
+ //Action
+ ActionLength: 1,
+ ActionValue: 0x01,
+ // End
+ EndBranch: 0x00,
+ }
+
+}
+
+// SetGenerteCaptureProtocolsreq is a structure for a request of "Protocol Filter/Capture Protocols" message
+type SetGenerteCaptureProtocolsreq struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+ ECLength uint8
+ ProtocolLength uint8
+ ProtocolValue uint8
+ ActionLength uint8
+ ActionValue uint8
+ EndBranch uint8
+}
+
+// Len returns the length of SetGenerteCaptureProtocolsreq
+func (d *SetGenerteCaptureProtocolsreq) Len() int {
+ return 21 + int(d.CTLength) + int(d.OCLength) + int(d.VcLength)
+}
+
+// LayerType returns the ethernet type of SetGenerteCaptureProtocolsreq
+func (d *SetGenerteCaptureProtocolsreq) LayerType() gopacket.LayerType {
+ return layers.LayerTypeEthernet
+}
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *SetGenerteCaptureProtocolsreq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Flags)
+ i += 2
+ data[i] = byte(d.OAMPDUCode)
+ i++
+ copy(data[i:i+len(d.OUId)], d.OUId)
+ i += len(d.OUId)
+ data[i] = byte(d.TOMIOpcode)
+ i++
+ data[i] = byte(d.CTBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.CTType)
+ i += 2
+ data[i] = byte(d.CTLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.CTInstance)
+ i += 4
+ data[i] = byte(d.OCBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.OCType)
+ i += 2
+ data[i] = byte(d.OCLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.OCInstance)
+ i += 4
+ data[i] = byte(d.VcBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VcLeaf)
+ i += 2
+ data[i] = byte(d.VcLength)
+ i++
+ data[i] = byte(d.ECLength)
+ i++
+ data[i] = byte(d.ProtocolLength)
+ i++
+ data[i] = byte(d.ProtocolValue)
+ i++
+ data[i] = byte(d.ActionLength)
+ i++
+ data[i] = byte(d.ActionValue)
+ i++
+ data[i] = byte(d.EndBranch)
+
+ return nil
+
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *SetGenerteCaptureProtocolsreq) Decode(data []byte) error {
+ i := 0
+ d.Opcode = data[i]
+ i++
+ d.Flags = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OAMPDUCode = data[i]
+ i++
+ d.OUId = data[i : i+3]
+ i += len(d.OUId)
+ d.TOMIOpcode = data[i]
+ i++
+ d.CTBranch = data[i]
+ i++
+ d.CTType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.CTLength = data[i]
+ i++
+ d.CTInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.OCBranch = data[i]
+ i++
+ d.OCType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OCLength = data[i]
+ i++
+ d.OCInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.VcBranch = data[i]
+ i++
+ d.VcLeaf = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.VcLength = data[i]
+ i++
+ d.ECLength = data[i]
+ i++
+ d.ProtocolLength = data[i]
+ i++
+ d.ProtocolValue = data[i]
+ i++
+ d.ActionLength = data[i]
+ i++
+ d.ActionValue = data[i]
+ i++
+ d.EndBranch = data[i]
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/msg_set_default_outlet.go b/internal/pkg/core/l2oam/msg_set_default_outlet.go
new file mode 100644
index 0000000..a794903
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_default_outlet.go
@@ -0,0 +1,168 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// GenerateSetDefaultOutlet generates "Default Outlet" message
+func GenerateSetDefaultOutlet(oc *TomiObjectContext, onuID *TomiObjectContext) gopacket.SerializableLayer {
+
+ tibitData := &DefaultOutletRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: oc.Branch,
+ OCType: oc.Type,
+ OCLength: oc.Length,
+ OCInstance: oc.Instance,
+ // Vc
+ VcBranch: 0x5d,
+ VcLeaf: 0x0003,
+ VcLength: 0x09,
+ // Default Outlet
+ DOLength: 8,
+ DOBranch: 0x0c,
+ DOType: 0x0011,
+ DOValLength: 4,
+ DOInstance: onuID.Instance,
+ // End
+ EndBranch: 0x00,
+ }
+
+ return tibitData
+}
+
+// DefaultOutletRequest is a structure for a request of "Default Outlet" message
+type DefaultOutletRequest struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+ DOLength uint8
+ DOBranch uint8
+ DOType uint16
+ DOValLength uint8
+ DOInstance uint32
+ EndBranch uint8
+}
+
+// String returns the string expression of DefaultOutletRequest
+func (d *DefaultOutletRequest) String() string {
+ message := fmt.Sprintf("Opcode:%x, Flags:%x, OAMPDUCode:%x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, CTBranch:%x, CTType:%x, CTLength:%x, CTInstance:%x", message, d.CTBranch, d.CTType, d.CTLength, d.CTInstance)
+ message = fmt.Sprintf("%s, OCBranch:%x, OCType:%x, OCLength:%x, OCInstance:%x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VcBranch:%x, VcLeaf:%x, VcLength:%x", message, d.VcBranch, d.VcLeaf, d.VcLength)
+ message = fmt.Sprintf("%s, DOLength:%x, DOBranch:%x, DOType:%x, DOValLength:%x, DOInstance:%x", message, d.DOLength, d.DOBranch, d.DOType, d.DOValLength, d.DOInstance)
+
+ return message
+}
+
+// Len returns the length of DefaultOutletRequest
+func (d *DefaultOutletRequest) Len() int {
+ return 38
+}
+
+// LayerType returns the ethernet type of DefaultOutletRequest
+func (d *DefaultOutletRequest) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *DefaultOutletRequest) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Flags)
+ i += 2
+ data[i] = byte(d.OAMPDUCode)
+ i++
+ copy(data[i:i+len(d.OUId)], d.OUId)
+ i += len(d.OUId)
+ data[i] = byte(d.TOMIOpcode)
+ i++
+ data[i] = byte(d.CTBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.CTType)
+ i += 2
+ data[i] = byte(d.CTLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.CTInstance)
+ i += 4
+ data[i] = byte(d.OCBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.OCType)
+ i += 2
+ data[i] = byte(d.OCLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.OCInstance)
+ i += 4
+ data[i] = byte(d.VcBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VcLeaf)
+ i += 2
+ data[i] = byte(d.VcLength)
+ i++
+ data[i] = byte(d.DOLength)
+ i++
+ data[i] = byte(d.DOBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.DOType)
+ i += 2
+ data[i] = byte(d.DOValLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.DOInstance)
+ i += 4
+ data[i] = byte(d.EndBranch)
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/msg_set_hbtx_period.go b/internal/pkg/core/l2oam/msg_set_hbtx_period.go
new file mode 100644
index 0000000..f2b784a
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_hbtx_period.go
@@ -0,0 +1,57 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetHbtxPeriod generates "OAM/HBTx Period"
+func GenerateSetHbtxPeriod() gopacket.SerializableLayer {
+
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0007,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vc
+ VcBranch: 0x0a,
+ VcLeaf: 0x0002,
+ VcLength: 3,
+ // EC
+ ECLength: 2,
+ ECValue: []byte{0x03, 0xe8},
+ // End
+ EndBranch: 0x00,
+ }
+
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_set_hbtx_template.go b/internal/pkg/core/l2oam/msg_set_hbtx_template.go
new file mode 100644
index 0000000..f319e52
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_hbtx_template.go
@@ -0,0 +1,62 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+//GenerateSetHbtxTemplate generates "OAM/HBTx Template" message
+func GenerateSetHbtxTemplate() gopacket.SerializableLayer {
+
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0007,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vc
+ VcBranch: 0x0a,
+ VcLeaf: 0x0003,
+ VcLength: 35,
+ // EC
+ ECLength: 34,
+ ECValue: []byte{
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02, 0xe8, 0xb4,
+ 0x70, 0x70, 0x04, 0x07, 0x88, 0x09, 0x03, 0x00,
+ 0x50, 0x00, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00,
+ 0x1b, 0x04, 0xb0, 0x2a, 0xea, 0x15, 0x00, 0x00,
+ 0x00, 0x23},
+ // End
+ EndBranch: 0x00,
+ }
+
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_set_ingress_port.go b/internal/pkg/core/l2oam/msg_set_ingress_port.go
new file mode 100644
index 0000000..d55efdc
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_ingress_port.go
@@ -0,0 +1,211 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// GenerateIngressPort generates "Protocol Filter/Ingress Port" message
+func GenerateIngressPort(OCInstance uint32, isponport bool) gopacket.SerializableLayer {
+
+ var ponPort uint16 = 0x0007
+
+ if !isponport {
+ ponPort = 0x0e07
+ }
+
+ return &SetGenerteIngressPortreq{
+ //IEEE 1904.2
+ Opcode: 0x03,
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ //TiBiT OLT Management Interface
+ TOMIOpcode: 0x03,
+ //Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ //Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0cff,
+ OCLength: 4,
+ OCInstance: OCInstance,
+ //Vc
+ VcBranch: 0xcf,
+ VcLeaf: 0x0002,
+ VcLength: 9,
+ //EC OC
+ ECOCLength1: 8,
+ ECOCBranch: 0x0c,
+ ECOCType: ponPort,
+ ECOCLength2: 4,
+ ECOCInstance: 0x00000000,
+ //End
+ EndBranch: 0x00,
+ }
+
+}
+
+// SetGenerteIngressPortreq is a structure for a reqest of "Protocol Filter/Ingress Port" message
+type SetGenerteIngressPortreq struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+ ECOCLength1 uint8
+ ECOCBranch uint8
+ ECOCType uint16
+ ECOCLength2 uint8
+ ECOCInstance uint32
+ EndBranch uint8
+}
+
+// Len returns the length of SetGenerteIngressPortreq
+func (d *SetGenerteIngressPortreq) Len() int {
+ return 21 + int(d.CTLength) + int(d.OCLength) + int(d.VcLength)
+}
+
+// LayerType returns the ethernet type of SetGenerteIngressPortreq
+func (d *SetGenerteIngressPortreq) LayerType() gopacket.LayerType {
+ return layers.LayerTypeEthernet
+}
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *SetGenerteIngressPortreq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Flags)
+ i += 2
+ data[i] = byte(d.OAMPDUCode)
+ i++
+ copy(data[i:i+len(d.OUId)], d.OUId)
+ i += len(d.OUId)
+ data[i] = byte(d.TOMIOpcode)
+ i++
+ data[i] = byte(d.CTBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.CTType)
+ i += 2
+ data[i] = byte(d.CTLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.CTInstance)
+ i += 4
+ data[i] = byte(d.OCBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.OCType)
+ i += 2
+ data[i] = byte(d.OCLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.OCInstance)
+ i += 4
+ data[i] = byte(d.VcBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VcLeaf)
+ i += 2
+ data[i] = byte(d.VcLength)
+ i++
+ data[i] = byte(d.ECOCLength1)
+ i++
+ data[i] = byte(d.ECOCBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.ECOCType)
+ i += 2
+ data[i] = byte(d.ECOCLength2)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.ECOCInstance)
+ i += 4
+ data[i] = byte(d.EndBranch)
+
+ return nil
+
+}
+
+// Decode decodes byte arrays to a data structure
+func (d *SetGenerteIngressPortreq) Decode(data []byte) error {
+ i := 0
+ d.Opcode = data[i]
+ i++
+ d.Flags = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OAMPDUCode = data[i]
+ i++
+ d.OUId = data[i : i+3]
+ i += len(d.OUId)
+ d.TOMIOpcode = data[i]
+ i++
+ d.CTBranch = data[i]
+ i++
+ d.CTType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.CTLength = data[i]
+ i++
+ d.CTInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.OCBranch = data[i]
+ i++
+ d.OCType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OCLength = data[i]
+ i++
+ d.OCInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.VcBranch = data[i]
+ i++
+ d.VcLeaf = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.VcLength = data[i]
+ i++
+ d.ECOCLength1 = data[i]
+ i++
+ d.ECOCBranch = data[i]
+ i++
+ d.ECOCType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.ECOCLength2 = data[i]
+ i++
+ d.ECOCInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.EndBranch = data[i]
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/msg_set_management_lock.go b/internal/pkg/core/l2oam/msg_set_management_lock.go
new file mode 100644
index 0000000..c1490cf
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_management_lock.go
@@ -0,0 +1,61 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetManagementLock generates "Device/ManagementLock" message
+func GenerateSetManagementLock() gopacket.SerializableLayer {
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0dce,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vc
+ VcBranch: 0xde,
+ VcLeaf: 0x002e,
+ VcLength: 0x04,
+ // EC
+ ECLength: 1,
+ ECValue: []byte{0x00},
+ ECList: []ECValueSet{
+ {
+ Length: 0x01,
+ Value: []byte{0x02},
+ },
+ },
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_set_mpcp_sync.go b/internal/pkg/core/l2oam/msg_set_mpcp_sync.go
new file mode 100644
index 0000000..bfa1d4d
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_mpcp_sync.go
@@ -0,0 +1,56 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetMPCPSync generates "MPCP/Sync Type" message
+func GenerateSetMPCPSync() gopacket.SerializableLayer {
+
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x43,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0007,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vc
+ VcBranch: 0xcc,
+ VcLeaf: 0x0004,
+ VcLength: 2,
+ // EC
+ ECLength: 1,
+ ECValue: []byte{0x20},
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_set_request_saved.go b/internal/pkg/core/l2oam/msg_set_request_saved.go
new file mode 100644
index 0000000..6869a79
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_request_saved.go
@@ -0,0 +1,56 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+//
+func GenerateSetRequestSaved() gopacket.SerializableLayer {
+
+ tibitData := &TOAMSetRequest{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x43,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x0007,
+ OCLength: 4,
+ OCInstance: 0x00000000,
+ // Vc
+ VcBranch: 0xcc,
+ VcLeaf: 0x0004,
+ VcLength: 2,
+ // EC
+ ECLength: 1,
+ ECValue: []byte{0x20},
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_set_traffic_profile.go b/internal/pkg/core/l2oam/msg_set_traffic_profile.go
new file mode 100644
index 0000000..af11a66
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_set_traffic_profile.go
@@ -0,0 +1,62 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetTrafficProfile generates "Traffic Control/Traffic Profile" message
+func GenerateSetTrafficProfile(trafficControl []byte, trafficProfile []byte) gopacket.SerializableLayer {
+ tibitData := &setTrafficControlTrafficProfileReq{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OAM Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15},
+ // TiBit OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x07c0,
+ OCLength: 4,
+ OCInstance: trafficControl,
+ // Vc
+ VcBranch: 0x7c,
+ VcLeaf: 0x0002,
+ VcLength: 9,
+ // EC OC
+ ECOC: []ECOCTrafficControlTrafficProfile{
+ {
+ EcOcLength: 8,
+ EcOcBranch: 0x0c,
+ EcOcType: 0x070f,
+ EcOcLength2: 4,
+ EcOcInstance: trafficProfile,
+ },
+ },
+ // End
+ EndBranch: 0x00,
+ }
+ return tibitData
+}
diff --git a/internal/pkg/core/l2oam/msg_toam_get.go b/internal/pkg/core/l2oam/msg_toam_get.go
new file mode 100644
index 0000000..f831aa0
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_toam_get.go
@@ -0,0 +1,185 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// TOAMGetRequest is a structure for GET request of TOAM message
+type TOAMGetRequest struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VdBranch uint8
+ VdLeaf uint16
+ EndBranch uint8
+}
+
+// String returns the string expression of TOAMGetRequest
+func (d *TOAMGetRequest) String() string {
+ message := fmt.Sprintf("Opcode:%02x, Flags:%04x, OAMPDUCode:%02x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%02x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, CTBranch:%02x, CTType:%04x, CTLength:%02x, CTInstance:%08x", message, d.CTBranch, d.CTType, d.CTLength, d.CTInstance)
+ message = fmt.Sprintf("%s, OCBranch:%02x, OCType:%04x, OCLength:%02x, OCInstance:%08x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VdBranch:%02x, VdLeaf:%04x, EndBranch:%02x", message, d.VdBranch, d.VdLeaf, d.EndBranch)
+ return message
+}
+
+// Len returns the length of TOAMGetRequest
+func (d *TOAMGetRequest) Len() int {
+ return 29
+}
+
+// LayerType returns the ethernet type of TOAMGetRequest
+func (d *TOAMGetRequest) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *TOAMGetRequest) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Flags)
+ i += 2
+ data[i] = byte(d.OAMPDUCode)
+ i++
+ copy(data[i:i+len(d.OUId)], d.OUId)
+ i += len(d.OUId)
+ data[i] = byte(d.TOMIOpcode)
+ i++
+ data[i] = byte(d.CTBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.CTType)
+ i += 2
+ data[i] = byte(d.CTLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.CTInstance)
+ i += 4
+ data[i] = byte(d.OCBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.OCType)
+ i += 2
+ data[i] = byte(d.OCLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.OCInstance)
+ i += 4
+ data[i] = byte(d.VdBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VdLeaf)
+ i += 2
+ data[i] = byte(d.EndBranch)
+
+ return nil
+}
+
+// TOAMGetResponse is a structure for GET response of TOAM message
+type TOAMGetResponse struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+}
+
+// String returns the string expression of TOAMGetResponse
+func (d *TOAMGetResponse) String() string {
+ message := fmt.Sprintf("Opcode:%02x, Flags:%04x, OAMPDUCode:%02x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%02x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, CTBranch:%02x, CTType:%04x, CTLength:%02x, CTInstance:%08x", message, d.CTBranch, d.CTType, d.CTLength, d.CTInstance)
+ message = fmt.Sprintf("%s, OCBranch:%02x, OCType:%04x, OCLength:%02x, OCInstance:%08x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VcBranch:%02x, VcLeaf:%04x, VcLength:%02x", message, d.VcBranch, d.VcLeaf, d.VcLength)
+ return message
+}
+
+// Len returns the length of TOAMGetResponse
+func (d *TOAMGetResponse) Len() int {
+ return 28
+}
+
+// LayerType returns the ethernet type of TOAMGetResponse
+func (d *TOAMGetResponse) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// Decode decodes byte arrays to a data structure
+func (d *TOAMGetResponse) Decode(data []byte) {
+ i := 0
+ d.Opcode = data[i]
+ i++
+ d.Flags = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OAMPDUCode = data[i]
+ i++
+ d.OUId = data[i : i+3]
+ i += len(d.OUId)
+ d.TOMIOpcode = data[i]
+ i++
+ d.CTBranch = data[i]
+ i++
+ d.CTType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.CTLength = data[i]
+ i++
+ d.CTInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.OCBranch = data[i]
+ i++
+ d.OCType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OCLength = data[i]
+ i++
+ d.OCInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.VcBranch = data[i]
+ i++
+ d.VcLeaf = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.VcLength = data[i]
+}
diff --git a/internal/pkg/core/l2oam/msg_toam_set.go b/internal/pkg/core/l2oam/msg_toam_set.go
new file mode 100644
index 0000000..7aa6bec
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_toam_set.go
@@ -0,0 +1,223 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// TOAMSetRequest is a structure for SET request of TOAM message
+type TOAMSetRequest struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+ ECLength uint8
+ ECValue []byte
+ ECList []ECValueSet
+ EndBranch uint8
+}
+
+// ECValueSet is a structure for Vc-EC object
+type ECValueSet struct {
+ Length uint8
+ Value []byte
+}
+
+// String returns the string expression of TOAMSetRequest
+func (d *TOAMSetRequest) String() string {
+ message := fmt.Sprintf("Opcode:%x, Flags:%x, OAMPDUCode:%x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, CTBranch:%x, CTType:%x, CTLength:%x, CTInstance:%x", message, d.CTBranch, d.CTType, d.CTLength, d.CTInstance)
+ message = fmt.Sprintf("%s, OCBranch:%x, OCType:%x, OCLength:%x, OCInstance:%x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VcBranch:%x, VcLeaf:%x, VcLength:%x", message, d.VcBranch, d.VcLeaf, d.VcLength)
+ message = fmt.Sprintf("%s, ECLength:%x, ECValue:%v, EndBranch:%x", message, d.ECLength, hex.EncodeToString(d.ECValue), d.EndBranch)
+ return message
+}
+
+// Len returns the length of TOAMSetRequest
+func (d *TOAMSetRequest) Len() int {
+ if d.VcLength != 0x80 {
+ return 22 + int(d.CTLength) + int(d.OCLength) + int(d.VcLength)
+ }
+ return 22 + int(d.CTLength) + int(d.OCLength)
+
+}
+
+// LayerType returns the ethernet type of TOAMSetRequest
+func (d *TOAMSetRequest) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *TOAMSetRequest) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Flags)
+ i += 2
+ data[i] = byte(d.OAMPDUCode)
+ i++
+ copy(data[i:i+len(d.OUId)], d.OUId)
+ i += len(d.OUId)
+ data[i] = byte(d.TOMIOpcode)
+ i++
+ data[i] = byte(d.CTBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.CTType)
+ i += 2
+ data[i] = byte(d.CTLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.CTInstance)
+ i += 4
+ data[i] = byte(d.OCBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.OCType)
+ i += 2
+ data[i] = byte(d.OCLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.OCInstance)
+ i += 4
+ data[i] = byte(d.VcBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VcLeaf)
+ i += 2
+ data[i] = byte(d.VcLength)
+ i++
+ if d.VcLength != 0x80 {
+ // EC
+ data[i] = byte(d.ECLength)
+ i++
+ copy(data[i:i+int(d.ECLength)], d.ECValue)
+ i += int(d.ECLength)
+ }
+ if d.ECList != nil {
+ for _, ecSet := range d.ECList {
+ data[i] = byte(ecSet.Length)
+ i++
+ copy(data[i:i+int(ecSet.Length)], ecSet.Value)
+ i += int(ecSet.Length)
+ }
+ }
+
+ data[i] = byte(d.EndBranch)
+
+ return nil
+}
+
+// TOAMSetResponse is a structure for SET response of TOAM message
+type TOAMSetResponse struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance uint32
+ VcBranch uint8
+ VcLeaf uint16
+ VcTOMIResCode uint8
+ EndBranch uint8
+}
+
+// String returns the string expression of TOAMSetResponse
+func (d *TOAMSetResponse) String() string {
+ message := fmt.Sprintf("Opcode:%x, Flags:%x, OAMPDUCode:%x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, CTBranch:%x, CTType:%x, CTLength:%x, CTInstance:%x", message, d.CTBranch, d.CTType, d.CTLength, d.CTInstance)
+ message = fmt.Sprintf("%s, OCBranch:%x, OCType:%x, OCLength:%x, OCInstance:%x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VcBranch:%x, VcLeaf:%x, VcTOMIResCode:%x, EndBranch:%x", message, d.VcBranch, d.VcLeaf, d.VcTOMIResCode, d.EndBranch)
+ return message
+}
+
+// Len returns the length of TOAMSetResponse
+func (d *TOAMSetResponse) Len() int {
+ return 34 + int(d.CTLength) + int(d.OCLength)
+}
+
+// LayerType returns the ethernet type of TOAMSetResponse
+func (d *TOAMSetResponse) LayerType() gopacket.LayerType { return layers.LayerTypeEthernet }
+
+// Decode decodes byte arrays to a data structure
+func (d *TOAMSetResponse) Decode(data []byte) error {
+ i := 0
+ d.Opcode = data[i]
+ i++
+ d.Flags = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OAMPDUCode = data[i]
+ i++
+ d.OUId = data[i : i+3]
+ i += len(d.OUId)
+ d.TOMIOpcode = data[i]
+ i++
+ d.CTBranch = data[i]
+ i++
+ d.CTType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.CTLength = data[i]
+ i++
+ d.CTInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.OCBranch = data[i]
+ i++
+ d.OCType = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.OCLength = data[i]
+ i++
+ d.OCInstance = binary.BigEndian.Uint32(data[i : i+4])
+ i += 4
+ d.VcBranch = data[i]
+ i++
+ d.VcLeaf = binary.BigEndian.Uint16(data[i : i+2])
+ i += 2
+ d.VcTOMIResCode = data[i]
+ i++
+ d.EndBranch = data[i]
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/msg_traffic_control_traffic_profile.go b/internal/pkg/core/l2oam/msg_traffic_control_traffic_profile.go
new file mode 100644
index 0000000..84a325a
--- /dev/null
+++ b/internal/pkg/core/l2oam/msg_traffic_control_traffic_profile.go
@@ -0,0 +1,188 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+// GenerateTrafficControlTrafficProfile generates "Traffic Control/Traffic Profile" message
+func GenerateTrafficControlTrafficProfile(trafficControl []byte, trafficProfile []byte) gopacket.SerializableLayer {
+
+ tibitData := &setTrafficControlTrafficProfileReq{
+ // IEEE 1904.2
+ Opcode: 0x03,
+ // OMI Protocol
+ Flags: 0x0050,
+ OAMPDUCode: 0xfe,
+ OUId: []byte{0x2a, 0xea, 0x15}, // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ // TiBiT OLT Management Interface
+ TOMIOpcode: 0x03,
+ // Correlation Tag
+ CTBranch: 0x0c,
+ CTType: 0x0c7a,
+ CTLength: 4,
+ CTInstance: getOltInstance(),
+ // Object Context
+ OCBranch: 0x0c,
+ OCType: 0x07c0,
+ OCLength: 4,
+ OCInstance: trafficControl,
+ // Vc
+ VcBranch: 0x7c,
+ VcLeaf: 0x0002,
+ VcLength: 0x09,
+
+ //EC OC
+ ECOC: []ECOCTrafficControlTrafficProfile{{EcOcLength: 8, EcOcBranch: 0x0c, EcOcType: 0x070f, EcOcLength2: 4, EcOcInstance: trafficProfile}},
+
+ // End
+ EndBranch: 0x00,
+ }
+
+ return tibitData
+}
+
+type setTrafficControlTrafficProfileReq struct {
+ layers.BaseLayer
+ Opcode uint8
+ Flags uint16
+ OAMPDUCode uint8
+ OUId []byte // Organizationally Unique Identifier: 2a:ea:15 (Tibit Communications)
+ TOMIOpcode uint8
+ CTBranch uint8
+ CTType uint16
+ CTLength uint8
+ CTInstance uint32
+ OCBranch uint8
+ OCType uint16
+ OCLength uint8
+ OCInstance []byte
+ VcBranch uint8
+ VcLeaf uint16
+ VcLength uint8
+
+ ECOC []ECOCTrafficControlTrafficProfile
+
+ EndBranch uint8
+}
+
+// ECOCTrafficControlTrafficProfile is a structure for Vc-EC OC object
+type ECOCTrafficControlTrafficProfile struct {
+ EcOcLength uint8
+ EcOcBranch uint8
+ EcOcType uint16
+ EcOcLength2 uint8
+ EcOcInstance []byte
+}
+
+// String returns the string expression of setTrafficControlTrafficProfileReq
+func (d *setTrafficControlTrafficProfileReq) String() string {
+ message := fmt.Sprintf("Opcode:%x, Flags:%x, OAMPDUCode:%x, OUId:%v", d.Opcode, d.Flags, d.OAMPDUCode, hex.EncodeToString(d.OUId))
+ message = fmt.Sprintf("%s, TOMIOpcode:%x", message, d.TOMIOpcode)
+ message = fmt.Sprintf("%s, CTBranch:%x, CTType:%x, CTLength:%x, CTInstance:%x", message, d.CTBranch, d.CTType, d.CTLength, d.CTInstance)
+ message = fmt.Sprintf("%s, OCBranch:%x, OCType:%x, OCLength:%x, OCInstance:%x", message, d.OCBranch, d.OCType, d.OCLength, d.OCInstance)
+ message = fmt.Sprintf("%s, VcBranch:%x, VcLeaf:%x, VcLength:%x", message, d.VcBranch, d.VcLeaf, d.VcLength)
+ for _, ecoc := range d.ECOC {
+ message = fmt.Sprintf("%s, EcOcLength:%x, EcOcBranch:%v, EcOcType:%x, EcOcLength2:%x, EcOcInstance:%x, ", message, ecoc.EcOcLength, ecoc.EcOcBranch, ecoc.EcOcType, ecoc.EcOcLength2, hex.EncodeToString(ecoc.EcOcInstance))
+ }
+ message = fmt.Sprintf("%s, EndBranch:%x", message, d.EndBranch)
+ return message
+}
+
+// Len returns the length of setTrafficControlTrafficProfileReq
+func (d *setTrafficControlTrafficProfileReq) Len() int {
+ return 21 + int(d.CTLength) + int(d.OCLength) + int(d.VcLength)
+}
+
+// LayerType returns the ethernet type of setTrafficControlTrafficProfileReq
+func (d *setTrafficControlTrafficProfileReq) LayerType() gopacket.LayerType {
+ return layers.LayerTypeEthernet
+}
+
+// SerializeTo serializes a data structure to byte arrays
+func (d *setTrafficControlTrafficProfileReq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+ plen := int(d.Len())
+ data, err := b.PrependBytes(plen)
+ if err != nil {
+ return err
+ }
+
+ i := 0
+ data[i] = byte(d.Opcode)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.Flags)
+ i += 2
+ data[i] = byte(d.OAMPDUCode)
+ i++
+ copy(data[i:i+len(d.OUId)], d.OUId)
+ i += len(d.OUId)
+ data[i] = byte(d.TOMIOpcode)
+ i++
+ data[i] = byte(d.CTBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.CTType)
+ i += 2
+ data[i] = byte(d.CTLength)
+ i++
+ binary.BigEndian.PutUint32(data[i:i+4], d.CTInstance)
+ i += 4
+ data[i] = byte(d.OCBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.OCType)
+ i += 2
+ data[i] = byte(d.OCLength)
+ i++
+ copy(data[i:i+4], d.OCInstance)
+ i += 4
+ data[i] = byte(d.VcBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], d.VcLeaf)
+ i += 2
+ data[i] = byte(d.VcLength)
+ i++
+ for _, ecoc := range d.ECOC {
+ nextIndex := SerializeECOC(&ecoc, data, i)
+ i = nextIndex
+ }
+
+ data[i] = byte(d.EndBranch)
+
+ return nil
+}
+
+// SerializeECOC serializes a "EC OC" structure to byte arrays
+func SerializeECOC(ecoc *ECOCTrafficControlTrafficProfile, data []byte, startIndex int) int {
+ i := startIndex
+ data[i] = byte(ecoc.EcOcLength)
+ i++
+ data[i] = byte(ecoc.EcOcBranch)
+ i++
+ binary.BigEndian.PutUint16(data[i:i+2], ecoc.EcOcType)
+ i += 2
+ data[i] = byte(ecoc.EcOcLength2)
+ i++
+ copy(data[i:i+int(ecoc.EcOcLength2)], ecoc.EcOcInstance)
+ i += int(ecoc.EcOcLength2)
+
+ return i
+}
diff --git a/internal/pkg/core/l2oam/onu_list_access.go b/internal/pkg/core/l2oam/onu_list_access.go
new file mode 100644
index 0000000..0ca4714
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_list_access.go
@@ -0,0 +1,137 @@
+/*
+ * 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 l2oam
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+)
+
+// JSON file name
+const jsonname = "/onu_list.json"
+
+// OnuStatus has ONU status
+type OnuStatus struct {
+ ID string `json:"id"`
+ AdminState string `json:"admin_state"`
+ OpeState string `json:"ope_state"`
+ ConnectState string `json:"con_state"`
+ MacAddress string `json:"mac_addr"`
+ RebootState string `json:"reboot_state"`
+}
+
+// ReadOnuStatusList reads JSON file
+func ReadOnuStatusList() ([]OnuStatus, error) {
+ bytes, err := ioutil.ReadFile(os.Getenv("HOME") + jsonname)
+ if err != nil && os.IsNotExist(err) {
+ return nil, nil
+ }
+ var onuList []OnuStatus
+ if err := json.Unmarshal(bytes, &onuList); err != nil {
+ return nil, err
+ }
+ return onuList, nil
+}
+
+// WriteOnuStatusList writes JSON file
+func WriteOnuStatusList(list []OnuStatus) error {
+ bytes, err := json.Marshal(list)
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(os.Getenv("HOME")+jsonname, bytes, 0644)
+}
+
+// AddOnu adds ONU to ONU status list
+func AddOnu(sts *OnuStatus) error {
+ list, err := ReadOnuStatusList()
+ if err != nil {
+ return err
+ }
+ if list == nil {
+ newList := []OnuStatus{*sts}
+ return WriteOnuStatusList(newList)
+ }
+ return WriteOnuStatusList(append(list, *sts))
+}
+
+// UpdateOnu updates ONU status
+func UpdateOnu(upSts *OnuStatus) error {
+ list, err := ReadOnuStatusList()
+ if (err != nil) || (list == nil) {
+ return err
+ }
+ newList := []OnuStatus{}
+ for _, sts := range list {
+ if sts.ID == upSts.ID {
+ newList = append(newList, *upSts)
+ } else {
+ newList = append(newList, sts)
+ }
+ }
+ return WriteOnuStatusList(newList)
+}
+
+// RemoveOnu removes ONU from ONU status list
+func RemoveOnu(id string) error {
+ list, err := ReadOnuStatusList()
+ if (err != nil) || (list == nil) {
+ return err
+ }
+ newList := []OnuStatus{}
+ for _, sts := range list {
+ if sts.ID != id {
+ newList = append(newList, sts)
+ }
+ }
+ return WriteOnuStatusList(newList)
+}
+
+// GetOnuFromDeviceID returns ONU status from ONU status list using its device ID
+func GetOnuFromDeviceID(id string) (*OnuStatus, error) {
+ list, err := ReadOnuStatusList()
+ if err != nil {
+ return nil, err
+ }
+ if list == nil {
+ return nil, nil
+ }
+ for _, sts := range list {
+ if sts.ID != id {
+ return &sts, nil
+ }
+ }
+ return nil, nil
+}
+
+// GetOnuFromMacAddr returns ONU status from ONU status list using its MAC address
+func GetOnuFromMacAddr(addr string) (*OnuStatus, error) {
+ list, err := ReadOnuStatusList()
+ if err != nil {
+ return nil, err
+ }
+ if list == nil {
+ return nil, nil
+ }
+ for _, sts := range list {
+ if sts.MacAddress == addr {
+ return &sts, nil
+ }
+ }
+ return nil, nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_get_onu_system_info.go b/internal/pkg/core/l2oam/onu_msg_get_onu_system_info.go
new file mode 100644
index 0000000..6dd13d8
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_get_onu_system_info.go
@@ -0,0 +1,76 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateOnuSystemInfo generates "ONU System Information" message
+func GenerateOnuSystemInfo(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x01,
+ 0xd7, 0x00, 0x06, 0x00, 0x00,
+ },
+ }
+
+ return data
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x00,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x40, 0x00, 0x00,
+ },
+ }
+
+ return data
+ }
+
+ return nil
+}
+
+// GetOnuSerialNo returns a serial number
+func GetOnuSerialNo(pkgType string, data []byte) string {
+ if pkgType == OnuPkgTypeA {
+ // 030050fe00100002 00 - 07
+ // d70006404d493920 08 - 15
+ // 32304b2042303041 16 - 23
+ // 3730204130302031 24 - 31
+ // 3730382020202020
+ // 2000000030313233
+ // 3435363730313233
+ // 3435363738394041
+ // 42434445
+ return string(data[20:26])
+ }
+ return ""
+}
+
+// GetOnuManufacture returns a manufacture information
+func GetOnuManufacture(pkgType string, data []byte) string {
+ if pkgType == OnuPkgTypeA {
+ if data[16] == 0x32 && data[17] == 0x30 && data[18] == 0x4b {
+ return "FURUKAWA"
+ }
+ }
+ return ""
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_get_unip_info.go b/internal/pkg/core/l2oam/onu_msg_get_unip_info.go
new file mode 100644
index 0000000..f3efa24
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_get_unip_info.go
@@ -0,0 +1,43 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateGetUnipInfo generates UnipInfo message
+func GenerateGetUnipInfo(pkgType string) gopacket.SerializableLayer {
+ if pkgType == OnuPkgTypeB {
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x00,
+ 0xca, 0xfe, 0x00, 0xb6, 0x00, 0x01, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0xb7, 0x00, 0x41,
+ },
+ }
+ }
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x01,
+ 0xd6, 0x00, 0x03, 0x01, 0x00, 0x07, 0x00, 0x20,
+ 0x07, 0x00, 0x5d, 0x07, 0x00, 0x5a, 0x07, 0x00,
+ 0x4f, 0x07, 0x00, 0xa3, 0x07, 0x00, 0x1a, 0x07,
+ 0x00, 0x47, 0x07, 0x00, 0xb0, 0x00, 0x00,
+ },
+ }
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_dyn_learning_mode.go b/internal/pkg/core/l2oam/onu_msg_set_dyn_learning_mode.go
new file mode 100644
index 0000000..33c7a00
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_dyn_learning_mode.go
@@ -0,0 +1,71 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateDynLearningMode generates "Dyn Learning Mode" message
+func GenerateDynLearningMode(PkgType string, index int) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA
+ if index == 1 {
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x01,
+ 0xd6, 0x00, 0x03, 0x01, 0x00, 0xd7, 0x01, 0x01,
+ 0x00, 0x00,
+ },
+ }
+ return data
+ } else if index == 2 {
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x03,
+ 0xd6, 0x00, 0x03, 0x01, 0x00, 0xd7, 0x01, 0x02,
+ 0x07, 0xd0, 0x00, 0x00,
+ },
+ }
+ return data
+ } else if index == 3 {
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x01,
+ 0xd6, 0x00, 0x03, 0x01, 0x00, 0xd7, 0x01, 0x03,
+ 0x00, 0x00,
+ },
+ }
+ return data
+ }
+
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x00,
+ 0xca, 0xfe, 0x00, 0xb6, 0x00, 0x01, 0x04, 0x01,
+ 0x00, 0x00, 0x01, 0xb7, 0x00, 0x1c, 0x00, 0x00,
+ },
+ }
+
+ return data
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_encryption_mode.go b/internal/pkg/core/l2oam/onu_msg_set_encryption_mode.go
new file mode 100644
index 0000000..599e8d4
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_encryption_mode.go
@@ -0,0 +1,61 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateEncryptionMode generates "Encryption Mode" message
+func GenerateEncryptionMode(PkgType string, index int) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA
+ if index == 1 {
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x03,
+ 0xd6, 0x00, 0x02, 0x01, 0x00, 0xd7, 0x04, 0x01,
+ 0x02, 0x01, 0x2c, 0x00, 0x00,
+ },
+ }
+ return data
+ } else if index == 2 {
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x03,
+ 0xd6, 0x00, 0x02, 0x01, 0x00, 0xd7, 0x04, 0x02,
+ 0x01, 0x02, 0x00, 0x00,
+ },
+ }
+ return data
+ }
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+ if index == 1 {
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x01, 0x01, 0x02,
+ },
+ }
+ return data
+ }
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_fec_mode.go b/internal/pkg/core/l2oam/onu_msg_set_fec_mode.go
new file mode 100644
index 0000000..22712ad
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_fec_mode.go
@@ -0,0 +1,49 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateFecMode generates "FEC Mode" message
+func GenerateFecMode(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x03,
+ 0xD6, 0x00, 0x02, 0x01, 0x00, 0xD7, 0x06, 0x05,
+ 0x02, 0x01, 0x01,
+ },
+ }
+ return data
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x16, 0x01, 0x01,
+ },
+ }
+
+ return data
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_onu_config.go b/internal/pkg/core/l2oam/onu_msg_set_onu_config.go
new file mode 100644
index 0000000..e648138
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_onu_config.go
@@ -0,0 +1,42 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateOnuConfig generates "ONU Config" message
+func GenerateOnuConfig(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA
+
+ return nil
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x00,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x45, 0x00, 0x00,
+ },
+ }
+ return data
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_onu_errored_frame_threshold.go b/internal/pkg/core/l2oam/onu_msg_set_onu_errored_frame_threshold.go
new file mode 100644
index 0000000..2649171
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_onu_errored_frame_threshold.go
@@ -0,0 +1,41 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateOnuErroredFrameThreshold generates "Errored Frame Threshold" message
+func GenerateOnuErroredFrameThreshold(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+
+ return nil
+
+ } else if PkgType == OnuPkgTypeB {
+
+ return &TibitFrame{Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x0d, 0x04, 0x00,
+ 0x00, 0xff, 0xff,
+ }}
+
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_onu_errored_frame_window.go b/internal/pkg/core/l2oam/onu_msg_set_onu_errored_frame_window.go
new file mode 100644
index 0000000..fa02acd
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_onu_errored_frame_window.go
@@ -0,0 +1,41 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateOnuErroredFrameWindow generates "Errored Frame Window" message
+func GenerateOnuErroredFrameWindow(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+
+ return nil
+
+ } else if PkgType == OnuPkgTypeB {
+
+ return &TibitFrame{Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x0c, 0x02, 0x00,
+ 0x0a,
+ }}
+
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_onu_priority_queue_count.go b/internal/pkg/core/l2oam/onu_msg_set_onu_priority_queue_count.go
new file mode 100644
index 0000000..e868f9c
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_onu_priority_queue_count.go
@@ -0,0 +1,61 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateOnuPriorityQueueCount generates "Priority Queue Count" message
+func GenerateOnuPriorityQueueCount(PkgType string, index int) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA
+ if index == 1 {
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x01,
+ 0xd7, 0x00, 0x0a, 0x00, 0x00,
+ },
+ }
+ return data
+ } else if index == 2 {
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x03,
+ 0xd6, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0xd7, 0x01, 0x0d, 0x08, 0x01, 0x01, 0xd0, 0x02,
+ 0x01, 0xd0, 0x01, 0x32, 0x00, 0x00,
+ },
+ }
+ return data
+ }
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x01, 0x01, 0x02,
+ },
+ }
+
+ return data
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_rx_input_power.go b/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_rx_input_power.go
new file mode 100644
index 0000000..8c498f2
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_rx_input_power.go
@@ -0,0 +1,50 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GeneratePonpOptRxInputPower generates "Ponp Optical Rx Input Power" message
+func GeneratePonpOptRxInputPower(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x01,
+ 0xD6, 0x00, 0x01, 0x01, 0x00, 0xD7, 0x02, 0x21,
+ 0x00, 0x00,
+ },
+ }
+ return data
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x00,
+ 0xca, 0xfe, 0x00, 0xb6, 0x00, 0x01, 0x04, 0x01,
+ 0x00, 0x00, 0x01, 0xb7, 0x00, 0x34, 0x00, 0x00,
+ },
+ }
+
+ return data
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_trx_supply_voltage.go b/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_trx_supply_voltage.go
new file mode 100644
index 0000000..0fd9d15
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_trx_supply_voltage.go
@@ -0,0 +1,49 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GeneratePonpOptTRxSupplyVoltage generates "Ponp Optical TRx Supply Voltage" message
+func GeneratePonpOptTRxSupplyVoltage(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x01,
+ 0xD6, 0x00, 0x01, 0x01, 0x00, 0xD7, 0x02, 0x1E,
+ 0x00, 0x00,
+ },
+ }
+ return data
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x00,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x31, 0x00, 0x00,
+ },
+ }
+
+ return data
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_tx_bias_current.go b/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_tx_bias_current.go
new file mode 100644
index 0000000..e0edf9b
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_tx_bias_current.go
@@ -0,0 +1,50 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GeneratePonpOptTxBiasCurrent generates "Ponp Optical Tx Bias Current" message
+func GeneratePonpOptTxBiasCurrent(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x01,
+ 0xD6, 0x00, 0x01, 0x01, 0x00, 0xD7, 0x02, 0x1F,
+ 0x00, 0x00,
+ },
+ }
+ return data
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x00,
+ 0xca, 0xfe, 0x00, 0xb6, 0x00, 0x01, 0x04, 0x01,
+ 0x00, 0x00, 0x01, 0xb7, 0x00, 0x32, 0x00, 0x00,
+ },
+ }
+
+ return data
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_tx_output_power.go b/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_tx_output_power.go
new file mode 100644
index 0000000..7112c7d
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_ponp_opt_tx_output_power.go
@@ -0,0 +1,50 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GeneratePonpOptTxOutputPower generates "Ponp Optical Tx Output Power" message
+func GeneratePonpOptTxOutputPower(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x01,
+ 0xD6, 0x00, 0x01, 0x01, 0x00, 0xD7, 0x02, 0x20,
+ 0x00, 0x00,
+ },
+ }
+ return data
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x00,
+ 0xca, 0xfe, 0x00, 0xb6, 0x00, 0x01, 0x04, 0x01,
+ 0x00, 0x00, 0x01, 0xb7, 0x00, 0x33, 0x00, 0x00,
+ },
+ }
+
+ return data
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_ponp_temperature.go b/internal/pkg/core/l2oam/onu_msg_set_ponp_temperature.go
new file mode 100644
index 0000000..2f9064c
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_ponp_temperature.go
@@ -0,0 +1,50 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GeneratePonpTemperature generates "Ponp Temerature" message
+func GeneratePonpTemperature(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA 最後尾に0追加
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x01,
+ 0xD6, 0x00, 0x01, 0x01, 0x00, 0xD7, 0x02, 0x1D,
+ 0x00, 0x00,
+ },
+ }
+ return data
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x00,
+ 0xca, 0xfe, 0x00, 0xb6, 0x00, 0x01, 0x04, 0x01,
+ 0x00, 0x00, 0x01, 0xb7, 0x00, 0x30, 0x00, 0x00,
+ },
+ }
+
+ return data
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_qos_policer_burst.go b/internal/pkg/core/l2oam/onu_msg_set_qos_policer_burst.go
new file mode 100644
index 0000000..ce83c6e
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_qos_policer_burst.go
@@ -0,0 +1,42 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetQosPolicerBurst generates QosPolicerBurst message
+func GenerateSetQosPolicerBurst(pkgType string) gopacket.SerializableLayer {
+ if pkgType == OnuPkgTypeB {
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x10, 0x08, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ },
+ }
+ }
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x03,
+ 0xD6, 0x00, 0x01, 0x01, 0x00, 0xD7, 0x06, 0x01,
+ 0x00, 0x00, 0x4E, 0x20, 0x00, 0x00,
+ },
+ }
+
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_qos_policer_rate.go b/internal/pkg/core/l2oam/onu_msg_set_qos_policer_rate.go
new file mode 100644
index 0000000..d343366
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_qos_policer_rate.go
@@ -0,0 +1,84 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetQosPolicerRate generates QosPolicerRate message
+func GenerateSetQosPolicerRate(pkgType string, idx int) gopacket.SerializableLayer {
+ if pkgType == OnuPkgTypeB {
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x0F, 0x20, 0x00,
+ 0x01, 0x86, 0xa0, 0x00, 0x01, 0x86, 0xa0, 0x00,
+ 0x01, 0x86, 0xa0, 0x00, 0x01, 0x86, 0xa0, 0x00,
+ 0x01, 0x86, 0xa0, 0x00, 0x01, 0x86, 0xa0, 0x00,
+ 0x01, 0x86, 0xa0, 0x00, 0x01, 0x86, 0xa0,
+ },
+ }
+ }
+ switch idx {
+ case 1:
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x03,
+ 0xd6, 0x00, 0x03, 0x01, 0x00, 0xd7, 0x06, 0x02,
+ 0x0a, 0xff, 0x00, 0x01, 0x00, 0x00, 0x04,
+ },
+ }
+ case 2:
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x03,
+ 0xD6, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xD7,
+ 0x00, 0x24,
+ },
+ }
+ case 3:
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x03,
+ 0xD6, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xD7,
+ 0x00, 0x25,
+ },
+ }
+ case 4:
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x03,
+ 0xD6, 0x00, 0x03, 0x00, 0xD7, 0x06, 0x02, 0x0A,
+ 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ },
+ }
+ case 5:
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x03,
+ 0xD6, 0x00, 0x03, 0x00, 0xD7, 0x06, 0x03, 0x07,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ }
+ default:
+ return &TibitFrame{
+ Data: []byte{},
+ }
+ }
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_reset_onu.go b/internal/pkg/core/l2oam/onu_msg_set_reset_onu.go
new file mode 100644
index 0000000..f204ba7
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_reset_onu.go
@@ -0,0 +1,42 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetResetOnu generates reset ONU message
+func GenerateSetResetOnu(pkgType string) gopacket.SerializableLayer {
+ if pkgType == OnuPkgTypeB {
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb9, 0x00, 0x0e, 0x01, 0x01,
+ 0x00, 0x00,
+ },
+ }
+ }
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x03,
+ 0xd6, 0x00, 0x00, 0xd9, 0x00, 0x01, 0x80, 0x00,
+ 0x00,
+ },
+ }
+
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_traffic_enable.go b/internal/pkg/core/l2oam/onu_msg_set_traffic_enable.go
new file mode 100644
index 0000000..bdfdeed
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_traffic_enable.go
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetTrafficEnable generates "Traffic Enable" message
+func GenerateSetTrafficEnable(pkgType string) gopacket.SerializableLayer {
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x03,
+ 0xd6, 0x00, 0x02, 0x01, 0x00, 0xd9, 0x06, 0x01,
+ 0x80,
+ },
+ }
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_unip_link_mode.go b/internal/pkg/core/l2oam/onu_msg_set_unip_link_mode.go
new file mode 100644
index 0000000..7054436
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_unip_link_mode.go
@@ -0,0 +1,42 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetUnipLinkMode generates UnipLinkMode message
+func GenerateSetUnipLinkMode(pkgType string) gopacket.SerializableLayer {
+ if pkgType == OnuPkgTypeB {
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb6, 0x00, 0x01, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0xb7, 0x00, 0x11, 0x01, 0x24,
+ },
+ }
+ }
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x03,
+ 0xD6, 0x00, 0x03, 0x01, 0x00, 0xD7, 0x01, 0x05,
+ 0x04, 0x00, 0xDF,
+ },
+ }
+
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_unip_target_queue_priority.go b/internal/pkg/core/l2oam/onu_msg_set_unip_target_queue_priority.go
new file mode 100644
index 0000000..0947405
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_unip_target_queue_priority.go
@@ -0,0 +1,45 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateUnipTargetQueuePriority generates "UnipTargetQueuePriority" message
+func GenerateUnipTargetQueuePriority(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+ //TypeA
+
+ return nil
+ } else if PkgType == OnuPkgTypeB {
+ //TypeB
+
+ data := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x43, 0x08, 0x00,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ },
+ }
+
+ return data
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_unip_toscos_conversion.go b/internal/pkg/core/l2oam/onu_msg_set_unip_toscos_conversion.go
new file mode 100644
index 0000000..a1f3abe
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_unip_toscos_conversion.go
@@ -0,0 +1,40 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateUnipTOSCOSConversion generates "Unip TOSCOS Conversion" message
+func GenerateUnipTOSCOSConversion(PkgType string) gopacket.SerializableLayer {
+
+ if PkgType == OnuPkgTypeA {
+
+ return nil
+
+ } else if PkgType == OnuPkgTypeB {
+
+ return &TibitFrame{Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x35, 0x08, 0x00,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ }}
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_vlan_pon_vid_value.go b/internal/pkg/core/l2oam/onu_msg_set_vlan_pon_vid_value.go
new file mode 100644
index 0000000..416dd69
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_vlan_pon_vid_value.go
@@ -0,0 +1,98 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetVlanPonVIDValue generates VlanPonVIDValue message
+func GenerateSetVlanPonVIDValue(pkgType string, idx int, iTpidValue []byte, iVidValue []byte) gopacket.SerializableLayer {
+
+ if pkgType == OnuPkgTypeB {
+ tibitData := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x15, 0x02,
+ },
+ }
+
+ tibitData.Data = append(tibitData.Data, iVidValue...)
+
+ return tibitData
+ }
+ switch idx {
+ case 1: // Downstream Rule
+ tibitData := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x03,
+ 0xd6, 0x00, 0x03, 0x01, 0x00, 0xd7, 0x05, 0x01,
+ 0x02, 0x01, 0x0a, 0xd7, 0x05, 0x01, 0x07, 0x02,
+ 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0xd7, 0x05,
+ 0x01, 0x02, 0x03, 0x02, 0xd7, 0x05, 0x01, 0x0b,
+ 0x03, 0x04, 0x08, 0x00, 0x00, 0x00, 0x04,
+ },
+ }
+
+ // itpid must be 4 bytes length
+ tibitData.Data = append(tibitData.Data, iTpidValue...)
+ // ivid must be 2 bytes length
+ tibitData.Data = append(tibitData.Data, iVidValue...)
+
+ bytes := []byte{
+ 0xd7, 0x05, 0x01, 0x04, 0x03, 0x07, 0x08, 0x00,
+ 0xd7, 0x05, 0x01, 0x01, 0x00, 0xd9, 0x05, 0x02,
+ 0x80, 0x00,
+ }
+
+ tibitData.Data = append(tibitData.Data, bytes...)
+
+ return tibitData
+
+ case 2: // Upstream Rule
+ tibitData := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x00, 0x10, 0x00, 0x03,
+ 0xd6, 0x00, 0x01, 0x01, 0x00, 0xd7, 0x05, 0x01,
+ 0x02, 0x01, 0x0a, 0xd7, 0x05, 0x01, 0x07, 0x02,
+ 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0xd7, 0x05,
+ 0x01, 0x02, 0x03, 0x02, 0xd7, 0x05, 0x01, 0x0b,
+ 0x03, 0x04, 0x08, 0x00, 0x00, 0x00, 0x04,
+ },
+ }
+
+ // itpid must be 4 bytes length
+ tibitData.Data = append(tibitData.Data, iTpidValue...)
+ // ivid must be 2 bytes length
+ tibitData.Data = append(tibitData.Data, iVidValue...)
+
+ bytes := []byte{
+ 0xd7, 0x05, 0x01, 0x04, 0x03, 0x07, 0x08, 0x00,
+ 0xd7, 0x05, 0x01, 0x01, 0x00, 0xd9, 0x05, 0x02,
+ 0x80, 0x00,
+ }
+
+ tibitData.Data = append(tibitData.Data, bytes...)
+
+ return tibitData
+
+ default:
+ return &TibitFrame{
+ Data: []byte{},
+ }
+ }
+}
diff --git a/internal/pkg/core/l2oam/onu_msg_set_vlan_tag_filter.go b/internal/pkg/core/l2oam/onu_msg_set_vlan_tag_filter.go
new file mode 100644
index 0000000..980f444
--- /dev/null
+++ b/internal/pkg/core/l2oam/onu_msg_set_vlan_tag_filter.go
@@ -0,0 +1,76 @@
+/*
+ * 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 l2oam
+
+import (
+ "github.com/google/gopacket"
+)
+
+// GenerateSetVlanTagFilter generates VlanTagFilter message
+func GenerateSetVlanTagFilter(pkgType string, idx int, value []byte) gopacket.SerializableLayer {
+
+ if pkgType == OnuPkgTypeB {
+ switch idx {
+ case 1:
+ tibitData := &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x13, 0x03, 0x03,
+ }, //,0x81,0x00
+ }
+
+ tibitData.Data = append(tibitData.Data, value...)
+
+ return tibitData
+ case 2:
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xfe, 0x90, 0x82, 0x60, 0x03,
+ 0xca, 0xfe, 0x00, 0xb7, 0x00, 0x14, 0x03, 0x01,
+ 0x00, 0x64,
+ },
+ }
+ default:
+ return &TibitFrame{
+ Data: []byte{},
+ }
+ }
+ } else {
+ switch idx {
+ case 1:
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x03,
+ 0xD6, 0x00, 0x00, 0xD7, 0x05, 0x03, 0x03, 0x81,
+ 0x00, 0x01,
+ },
+ }
+ case 2:
+ return &TibitFrame{
+ Data: []byte{
+ 0x03, 0x00, 0x50, 0xFE, 0x00, 0x10, 0x00, 0x03,
+ 0xD6, 0x00, 0x00, 0xD7, 0x05, 0x04, 0x03, 0x88,
+ 0xa8, 0x00,
+ },
+ }
+ default:
+ return &TibitFrame{
+ Data: []byte{},
+ }
+ }
+ }
+}
diff --git a/internal/pkg/core/l2oam_device.go b/internal/pkg/core/l2oam_device.go
new file mode 100644
index 0000000..76b548f
--- /dev/null
+++ b/internal/pkg/core/l2oam_device.go
@@ -0,0 +1,3936 @@
+/*
+ * 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
+
+import (
+ "context"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/core/l2oam"
+ "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"
+)
+
+// DeviceNameOlt is a constant for OLT
+const DeviceNameOlt = "L2oamOltDevice"
+
+// DeviceNameOnu is a constant for ONU
+const DeviceNameOnu = "L2oamOnuDevice"
+
+// KeepAliveInterval is an interval time for keep-alive
+const KeepAliveInterval = 800
+
+// KeepAliveCounter is max count of not receiving keep-alive response
+const KeepAliveCounter = 10
+
+// ResponseTimer is wait time for receiving response message(seconds)
+const ResponseTimer = 20
+
+//KeepAliveStatusE is state of keep-alive message
+type KeepAliveStatusE int
+
+const (
+ KeepAliveDisable KeepAliveStatusE = iota
+ KeepAliveStep1
+ KeepAliveStep2
+ KeepAliveStep3
+)
+
+// map for L2oamDevice, its key is MAC address without colon
+var deviceMap map[string]L2oamDevice
+
+// map for L2oamDevice, its key is device-ID
+var deviceMacMap map[string]string
+
+var mapMutex sync.Mutex
+
+var l2oamDeviceHandler *DeviceHandler
+
+func init() {
+ deviceMap = map[string]L2oamDevice{}
+ deviceMacMap = map[string]string{}
+ monitoringOnuStatus()
+}
+
+// FindL2oamDevice returns a L2oamDevice using MAC address
+func FindL2oamDevice(macAddress string) L2oamDevice {
+ mapMutex.Lock()
+ defer mapMutex.Unlock()
+ mac := strings.Replace(macAddress, ":", "", -1)
+ onu, ok := deviceMap[mac]
+ if !ok {
+ return nil
+ }
+ return onu
+
+}
+
+// FindL2oamDeviceByDeviceID returns a L2oamDevice using device-ID
+func FindL2oamDeviceByDeviceID(deviceID string) L2oamDevice {
+ mapMutex.Lock()
+ defer mapMutex.Unlock()
+ mac, ok := deviceMacMap[deviceID]
+ if !ok {
+ return nil
+ }
+ onu, ok := deviceMap[mac]
+ if !ok {
+ return nil
+ }
+ return onu
+}
+
+// SetupL2oamDeleteFlag sets delete flag
+func SetupL2oamDeleteFlag(deviceID string) bool {
+ mapMutex.Lock()
+ defer mapMutex.Unlock()
+
+ mac, ok := deviceMacMap[deviceID]
+ if !ok {
+ return false
+ }
+ device, ok := deviceMap[mac]
+ if !ok {
+ return false
+ }
+ if device.getDeleteFlag() {
+ return false
+ }
+ device.setDeleteFlag()
+ deviceMap[mac] = device
+
+ return true
+}
+
+// NewL2oamOltDevice creates a new OLT device
+func NewL2oamOltDevice(macAddress string, dh *DeviceHandler) (L2oamDevice, error) {
+ mapMutex.Lock()
+ defer mapMutex.Unlock()
+
+ l2oamDeviceHandler = dh
+
+ mac := strings.Replace(macAddress, ":", "", -1)
+ _, ok := deviceMap[mac]
+ if !ok {
+ device := &L2oamOltDevice{
+ Base: DeviceBase{
+ ResCh: nil,
+ KeepAliveResCh: make(chan *layers.Ethernet, 100),
+ DstMac: mac,
+ DH: dh,
+ DeviceID: dh.device.Id,
+ StopCh: make(chan string, 1),
+ ActiveState: false,
+ Name: DeviceNameOlt,
+ },
+ L2SwitchDomainMap: map[uint32]*l2SwitchDomain{},
+ }
+ deviceMap[mac] = device
+ deviceMacMap[dh.device.Id] = mac
+ return device, nil
+ }
+ return nil, fmt.Errorf("NewL2oamOltDevice() Already registered. mac=%s", mac)
+}
+
+// NewL2oamOnuDevice creates a new ONU device
+func NewL2oamOnuDevice(macAddress string, dh *DeviceHandler) (L2oamDevice, error) {
+ mapMutex.Lock()
+ defer mapMutex.Unlock()
+
+ mac := strings.Replace(macAddress, ":", "", -1)
+ _, ok := deviceMap[mac]
+ if !ok {
+ device := &L2oamOnuDevice{
+ Base: DeviceBase{
+ ResCh: nil,
+ KeepAliveResCh: make(chan *layers.Ethernet, 100),
+ DstMac: mac,
+ DH: dh,
+ StopCh: make(chan string, 1),
+ ActiveState: true,
+ Name: DeviceNameOnu,
+ },
+ keepAliveResponse: make(chan string, 1),
+ MacAddress: mac,
+ }
+ deviceMap[mac] = device
+ return device, nil
+ }
+ return nil, fmt.Errorf("NewL2oamOnuDevice() Already registered. mac=%s", mac)
+}
+
+// DeleteAllDevice deletes all devices
+func DeleteAllDevice() {
+ mapMutex.Lock()
+ defer mapMutex.Unlock()
+
+ for _, value := range deviceMap {
+ value.delete(context.Background())
+ }
+ deviceMap = map[string]L2oamDevice{}
+ deviceMacMap = map[string]string{}
+}
+
+// DeleteDevice deletes device
+func DeleteDevice(deviceId string, deviceMac string) {
+ mapMutex.Lock()
+ defer mapMutex.Unlock()
+
+ _, ok := deviceMacMap[deviceId]
+ if ok {
+ delete(deviceMacMap, deviceId)
+ logger.Info(context.Background(), fmt.Sprintf("DeleteDevice() deviceMacMap. deviceid=%s, ", deviceId))
+ }
+ mac := strings.Replace(deviceMac, ":", "", -1)
+ _, ok = deviceMap[mac]
+ if ok {
+ delete(deviceMap, mac)
+ logger.Info(context.Background(), fmt.Sprintf("DeleteDevice() deviceMap. mac=%s, ", mac))
+ }
+}
+
+// L2oamDevice interface provides some methods for device
+// OLT and ONU implement each functions
+type L2oamDevice interface {
+ getDeviceName() string
+ send(message gopacket.SerializableLayer) error
+ waitResponse(timeoutSecond int) (*layers.Ethernet, error)
+ sendAndWait(message gopacket.SerializableLayer, timeoutSecond int) (*layers.Ethernet, error)
+ recieve(etherPacket *layers.Ethernet)
+ recieveKeepAlive(etherPacket *layers.Ethernet)
+ startKeepAlive()
+ stopKeepAlive()
+ setActiveState(ctx context.Context, isActive bool)
+ startMountSequence(ctx context.Context, pkgType string, cmd *L2oamCmd)
+ reboot(ctx context.Context)
+ setL2oamCmd(*L2oamCmd)
+ getL2oamCmd() *L2oamCmd
+ setObjectContext(*l2oam.TomiObjectContext)
+ getObjectContext() *l2oam.TomiObjectContext
+ setReferenceTable(*l2oam.GetTrafficControlReferenceTableRes)
+ getReferenceTable() *l2oam.GetTrafficControlReferenceTableRes
+ setProfile(*l2oam.SetGenericActionCreateRes, *l2oam.SetGenericActionCreateRes)
+ getProfile() (*l2oam.SetGenericActionCreateRes, *l2oam.SetGenericActionCreateRes)
+ delete(ctx context.Context)
+ setDeleteFlag()
+ getDeleteFlag() bool
+ setAutonomousFlag(bool)
+ getAutonomousFlag() bool
+ updateMap()
+ receiveEapol(etherPacket *layers.Ethernet)
+ addFlow(ctx context.Context, cmd *L2oamCmd) error
+ updateFlow(ctx context.Context, cmd *L2oamCmd) error
+ removeFlow(ctx context.Context) error
+}
+
+type l2SwitchDomain struct {
+ OcAction *l2oam.TomiObjectContext
+ Tpid []byte
+ Vid []byte
+ Onus map[string]bool
+}
+
+// DeviceBase ====================================
+// DeviceBase contains common functions
+// ===============================================
+type DeviceBase struct {
+ ResCh chan *layers.Ethernet
+ KeepAliveResCh chan *layers.Ethernet
+ DstMac string
+ DH *DeviceHandler
+ DeviceID string
+ mu sync.Mutex
+ muResponse sync.Mutex
+ muResCh sync.Mutex
+ StopCh chan string
+ ActiveState bool
+ KeepAliveStatus KeepAliveStatusE
+ KeepAliveRemoteValue []byte
+ KeepAliveSendCounter int
+ Name string
+ DeleteFlag bool
+ AutonomousFlag bool
+ FlowAdded bool
+ ActionIds map[int]uint32
+ EapFlag bool
+}
+
+const (
+ ActionIDFilterPonPort = 1
+ ActionIDFilterEthPort = 2
+)
+
+func (d *DeviceBase) getDeviceName() string {
+ return d.Name
+}
+
+func (d *DeviceBase) send(message gopacket.SerializableLayer) error {
+ var bytes []byte
+ if vlanEnable {
+ vlanLayer := &layers.Dot1Q{
+ VLANIdentifier: vlanIdManagement,
+ Type: 0xa8c8,
+ }
+ bytes = l2oam.CreateMessageVlan(
+ SrcMac,
+ convertMacString(d.DstMac),
+ 0x88a8,
+ message,
+ vlanLayer,
+ )
+ } else {
+ bytes = l2oam.CreateMessage(
+ SrcMac,
+ convertMacString(d.DstMac),
+ 0xa8c8,
+ message,
+ )
+
+ }
+ GetL2oamHandle().send(bytes)
+ return nil
+}
+
+func (d *DeviceBase) waitResponse(timeoutSecond int) (*layers.Ethernet, error) {
+ d.muResponse.Lock()
+ defer d.muResponse.Unlock()
+
+ d.ResCh = make(chan *layers.Ethernet, 1)
+ defer func() {
+ d.muResCh.Lock()
+ close(d.ResCh)
+ d.ResCh = nil
+ d.muResCh.Unlock()
+ }()
+ ctx := context.Background()
+ var res *layers.Ethernet
+ var err error
+ select {
+ case res = <-d.ResCh:
+ // DeviceBase do nothing
+ logger.Debug(ctx, fmt.Sprintf("[%s] DeviceBase.waitResponse() received", d.getDeviceName()))
+ case <-time.After(time.Duration(timeoutSecond) * time.Second):
+ err = fmt.Errorf("DeviceBase.waitResponse().ResCh receive timeout")
+ }
+ return res, err
+}
+
+func (d *DeviceBase) sendAndWait(message gopacket.SerializableLayer, timeoutSecond int) (*layers.Ethernet, error) {
+ if err := d.send(message); err != nil {
+ return nil, err
+ }
+
+ return d.waitResponse(timeoutSecond)
+}
+
+func (d *DeviceBase) recieve(etherPacket *layers.Ethernet) {
+ d.muResCh.Lock()
+ defer d.muResCh.Unlock()
+
+ if d.ResCh != nil {
+ if len(d.ResCh) == cap(d.ResCh) {
+ logger.Error(context.Background(), fmt.Sprintf("[%s] recieve() ResCh channel is full. MacAddress=%s", d.getDeviceName(), d.DstMac))
+ } else {
+ d.ResCh <- etherPacket
+ }
+ }
+}
+
+func (d *DeviceBase) recieveKeepAlive(etherPacket *layers.Ethernet) {
+ d.KeepAliveResCh <- etherPacket
+}
+
+func (d *DeviceBase) startKeepAlive() {
+ go func() {
+ ctx := context.Background()
+ d.KeepAliveStatus = KeepAliveStep1
+ d.countClearKeepAliveCounter()
+ logger.Debug(ctx, fmt.Sprintf("[%s] startKeepAlive(). MacAddress=%s, %v", d.getDeviceName(), d.DstMac, d))
+
+ for {
+ select {
+ case <-d.StopCh:
+ logger.Debug(ctx, fmt.Sprintf("[%s] startKeepAlive() StopCh request. MacAddress=%s", d.getDeviceName(), d.DstMac))
+ return
+ case etherPacket := <-d.KeepAliveResCh:
+ packet := &l2oam.OAMPDUInformation{}
+ if err := packet.Decode(etherPacket.Payload); err != nil {
+ break
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] receive KeepAlive() mac=%s", d.getDeviceName(), d.DstMac))
+
+ // discards received packet if the device is not active
+ if d.isEnable() {
+ // keep received LIValue for sending next messages
+ d.KeepAliveRemoteValue = packet.LIValue
+ if packet.Flags == 0x0028 {
+ d.KeepAliveStatus = KeepAliveStep2
+ } else {
+ d.KeepAliveStatus = KeepAliveStep3
+ }
+ d.setActiveState(ctx, true)
+ d.countClearKeepAliveCounter()
+ }
+
+ case <-time.After(KeepAliveInterval * time.Millisecond):
+
+ if !d.isActive() {
+ d.countClearKeepAliveCounter()
+ } else {
+ logger.Debug(ctx, fmt.Sprintf("[%s] KeepAlive timer expired. MacAddress=%s", d.getDeviceName(), d.DstMac))
+ // First three messages are sent each messages.
+ // After that, third message will be sent repeatedly
+ var err error = nil
+ if d.KeepAliveStatus == KeepAliveStep1 {
+ err = d.send(l2oam.GeneateKeepAlive1(l2oam.OnuPkgType == l2oam.OnuPkgTypeA))
+ } else if d.KeepAliveStatus == KeepAliveStep2 {
+ err = d.send(l2oam.GeneateKeepAlive2(d.KeepAliveRemoteValue, l2oam.OnuPkgType == l2oam.OnuPkgTypeA))
+ } else if d.KeepAliveStatus == KeepAliveStep3 {
+ err = d.send(l2oam.GeneateKeepAlive3(d.KeepAliveRemoteValue))
+ }
+ if err != nil {
+ break
+ }
+ d.countUpKeepAliveCounter()
+
+ if d.isKeepAliveLimitOver() {
+ d.setActiveState(ctx, false)
+ }
+ }
+
+ if d.EapFlag {
+ logger.Debug(ctx, fmt.Sprintf("[%s] EapFlag == true. MacAddress=%s", d.getDeviceName(), d.DstMac))
+ /*
+ onu := FindL2oamDevice(d.DstMac)
+ if onu == nil {
+ logger.Debug(ctx, "onu not found.")
+ } else {
+ go d.DH.L2oamAddFlowAndMount(ctx, d.DeviceID, []byte{0x64}, []byte{0x00, 0x64})
+ }
+ */
+ d.EapFlag = false
+ }
+ }
+ }
+ }()
+}
+
+func (d *DeviceBase) stopKeepAlive() {
+ if len(d.StopCh) == cap(d.StopCh) {
+ logger.Error(context.Background(), fmt.Sprintf("[%s] stopKeepAlive stop channel is full. MacAddress=%s", d.getDeviceName(), d.DstMac))
+ } else {
+ d.StopCh <- "Stop"
+ }
+}
+
+func (d *DeviceBase) countUpKeepAliveCounter() {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ d.KeepAliveSendCounter++
+}
+
+func (d *DeviceBase) countClearKeepAliveCounter() {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ d.KeepAliveSendCounter = 0
+}
+
+func (d *DeviceBase) isKeepAliveLimitOver() bool {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ return KeepAliveCounter <= d.KeepAliveSendCounter
+}
+
+func (d *DeviceBase) isActive() bool {
+ if !d.isEnable() {
+ return false
+ }
+ return d.ActiveState
+}
+
+func (d *DeviceBase) isEnable() bool {
+ if d.DeviceID == "" {
+ return true
+ }
+ device, err := d.DH.coreProxy.GetDevice(context.Background(), d.DeviceID, d.DeviceID)
+ if err != nil || device == nil {
+ logger.Debug(context.Background(), fmt.Sprintf("[%s] isEnable() coreProxy.GetDevice() deviceid=%s, error. %v", d.getDeviceName(), d.DeviceID, err))
+ return false
+ }
+ return device.AdminState == common.AdminState_ENABLED
+}
+
+func (d *DeviceBase) setActiveState(ctx context.Context, isActive bool) {
+ d.ActiveState = isActive
+
+ switch d.getDeviceName() {
+ case DeviceNameOlt:
+ if !isActive {
+ d.ActiveState = true
+ }
+ updateOltActiveStatus(ctx, d.DH, isActive)
+
+ case DeviceNameOnu:
+ // updateOnuActiveStatus(ctx, d.DstMac, isActive, d.DH)
+ updateOnuActiveStatus2(ctx, d.DH, isActive, d.DeviceID)
+ }
+}
+
+/*
+func (d *DeviceBase) startMountSequence(ctx context.Context, pkgType string, cmd *L2oamCmd) {
+}
+
+func (d *DeviceBase) reboot(ctx context.Context) {
+}
+*/
+
+func (d *DeviceBase) delete(ctx context.Context) {
+ logger.Info(ctx, fmt.Sprintf("[%s] delete() deviceid=%s, mac=%s", d.getDeviceName(), d.DeviceID, d.DstMac))
+ d.stopKeepAlive()
+}
+
+func (d *DeviceBase) setDeleteFlag() {
+ d.DeleteFlag = true
+}
+
+func (d *DeviceBase) getDeleteFlag() bool {
+ return d.DeleteFlag
+}
+
+func (d *DeviceBase) setAutonomousFlag(flag bool) {
+ d.AutonomousFlag = flag
+}
+
+func (d *DeviceBase) getAutonomousFlag() bool {
+ return d.AutonomousFlag
+}
+
+func (d *DeviceBase) receiveEapol(etherPacket *layers.Ethernet) {
+ go func() {
+ packetBytes := append(etherPacket.Contents, etherPacket.Payload...)
+ pktInd := &oop.PacketIndication{
+ IntfId: 0,
+ GemportId: 1,
+ PortNo: 16,
+ Pkt: packetBytes,
+ IntfType: "pon",
+ }
+ logger.Info(context.Background(), fmt.Sprintf("[%s] receiveEapol() deviceid=%s, pktInd=%x", d.getDeviceName(), d.DeviceID, pktInd))
+
+ if err := d.DH.handlePacketIndication(context.Background(), pktInd); err != nil {
+ logger.Error(context.Background(), fmt.Sprintf("[%s] receiveEapol() error. deviceid=%s, ", d.getDeviceName(), d.DeviceID))
+ }
+ }()
+}
+
+// L2oamOltDevice =================================
+// L2oamOltDevice provides functions for OLT device
+// ================================================
+type L2oamOltDevice struct {
+ Base DeviceBase
+ autonomousExecMutex sync.Mutex
+ L2oamCmd *L2oamCmd
+ ObjectContext *l2oam.TomiObjectContext
+ ReferenceTable *l2oam.GetTrafficControlReferenceTableRes
+ DownProfile *l2oam.SetGenericActionCreateRes
+ UpProfile *l2oam.SetGenericActionCreateRes
+ L2SwitchDomainMap map[uint32]*l2SwitchDomain
+}
+
+func (d *L2oamOltDevice) getDeviceName() string {
+ return d.Base.getDeviceName()
+}
+func (d *L2oamOltDevice) send(message gopacket.SerializableLayer) error {
+ return d.Base.send(message)
+}
+func (d *L2oamOltDevice) waitResponse(timeoutSecond int) (*layers.Ethernet, error) {
+ return d.Base.waitResponse(timeoutSecond)
+}
+func (d *L2oamOltDevice) sendAndWait(message gopacket.SerializableLayer, timeoutSecond int) (*layers.Ethernet, error) {
+ return d.Base.sendAndWait(message, timeoutSecond)
+}
+
+func (d *L2oamOltDevice) recieve(etherPacket *layers.Ethernet) {
+ bytes := etherPacket.Payload
+ opcode := bytes[0]
+ oampduCode := bytes[3]
+ tomiOpCode := bytes[7]
+ logger.Debug(context.Background(), fmt.Sprintf("[%s] recieve. opcode=%v oampduCode=%v, %x", d.getDeviceName(), opcode, oampduCode, bytes))
+
+ if opcode == 0x03 && oampduCode == 0xfe {
+ // Autonomous message
+ if tomiOpCode == 0xae {
+ go d.receiveAutonomounsEvent(etherPacket)
+ } else {
+ d.Base.recieve(etherPacket)
+ }
+ } else if opcode == 0xfd {
+ // other messages
+ d.Base.recieve(etherPacket)
+ } else {
+ logger.Error(context.Background(), fmt.Sprintf("[%s] receive error. invalid message %v", d.getDeviceName(), etherPacket))
+ }
+
+}
+func (d *L2oamOltDevice) recieveKeepAlive(etherPacket *layers.Ethernet) {
+ d.Base.recieveKeepAlive(etherPacket)
+}
+func (d *L2oamOltDevice) startKeepAlive() {
+ d.Base.startKeepAlive()
+}
+func (d *L2oamOltDevice) stopKeepAlive() {
+ d.Base.stopKeepAlive()
+}
+func (d *L2oamOltDevice) receiveAutonomounsEvent(etherPacket *layers.Ethernet) {
+ d.autonomousExecMutex.Lock()
+ defer d.autonomousExecMutex.Unlock()
+
+ packet := &l2oam.AutonomousEvent{}
+ if err := packet.Decode(etherPacket.Payload); err != nil {
+ return
+ }
+
+ if packet.IsRegistrationStatusMessage() {
+ if d.getAutonomousFlag() {
+ d.addOnu(packet)
+ } else {
+ logger.Debug(context.Background(), fmt.Sprintf("[%s] receiveAutonomounsEvent() skip. AutonomousFlag is false. ", d.getDeviceName()))
+ }
+ }
+}
+func (d *L2oamOltDevice) addOnu(packet *l2oam.AutonomousEvent) {
+ ctx := context.Background()
+ logger.Debug(ctx, fmt.Sprintf("[%s] addOnu() Start. ", d.getDeviceName()))
+ operIndication := &oop.Indication_IntfOperInd{
+ IntfOperInd: &oop.IntfOperIndication{
+ Type: "nni",
+ IntfId: 0,
+ OperState: "up",
+ },
+ }
+ indication := &oop.Indication{
+ Data: operIndication,
+ }
+ d.Base.DH.handleIndication(ctx, indication)
+ operIndication.IntfOperInd.Type = "pon"
+ d.Base.DH.handleIndication(ctx, indication)
+
+ oc := &l2oam.TomiObjectContext{
+ Branch: packet.ComResp.OCBranch,
+ Type: packet.ComResp.OCType,
+ Length: packet.ComResp.OCLength,
+ Instance: packet.ComResp.OCInstance,
+ }
+
+ ether, err := d.sendAndWait(l2oam.GenerateGetMpcpMacAddress(oc), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] addOnu() GetMpcpMacAddress Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ macPacket := &l2oam.GetMpcpMacAddressRes{}
+ if err = macPacket.Decode(ether.Payload); err != nil {
+ return
+ }
+ macAddress := macPacket.GetMacAddress()
+ logger.Debug(ctx, fmt.Sprintf("[%s] addOnu() GetMpcpMacAddress Success: macAddress=%s, response=%x:%s", d.getDeviceName(), macAddress, ether.Payload, macPacket))
+
+ var onu *L2oamOnuDevice
+ device := FindL2oamDevice(macAddress)
+ if device != nil {
+ logger.Debug(ctx, fmt.Sprintf("addOnu() FindL2oamDevice() is find. macAddress=%s", macAddress))
+ onu = device.(*L2oamOnuDevice)
+ if onu != nil {
+ logger.Debug(ctx, fmt.Sprintf("addOnu() assertion success. macAddress=%s, deviceId=%s", macAddress, onu.Base.DeviceID))
+ onu.setActiveState(ctx, true)
+ }
+ return
+ }
+
+ logger.Debug(ctx, fmt.Sprintf("addOnu() NewL2oamOnuDevice() called. macAddress=%s", macAddress))
+ device, _ = NewL2oamOnuDevice(macAddress, d.Base.DH)
+ onu = device.(*L2oamOnuDevice)
+
+ ether, err = d.sendAndWait(l2oam.GenerateGetMpcpLLId(oc), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] addOnu() GetMpcpLLId Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+
+ llidPacket := &l2oam.GetMpcpLLIdRes{}
+ if err = llidPacket.Decode(ether.Payload); err != nil {
+ return
+ }
+ llid := llidPacket.GetLLID()
+ logger.Debug(ctx, fmt.Sprintf("[%s] addOnu() GetMpcpLLId Success: LLID=%d, response=%x:%s", d.getDeviceName(), llid, ether.Payload, llidPacket))
+
+ vendorID := ""
+ VendorSpecific := ""
+ serialNumber := ""
+ onu.setActiveState(ctx, false)
+
+ if err := onu.send(l2oam.GeneateKeepAlive1(l2oam.OnuPkgType == l2oam.OnuPkgTypeA)); err != nil {
+ logger.Debug(ctx, fmt.Sprintf("error: %v", err))
+ }
+ go func() {
+
+ err = onu.waitKeepALiveResponse()
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] addOnu() onu keep alive error. %s", d.getDeviceName(), err))
+ DeleteDevice(onu.Base.DeviceID, onu.Base.DstMac)
+ return
+ }
+ onu.keepAliveResponse = nil
+ logger.Info(ctx, fmt.Sprintf("[%s] addOnu() onu keep alive success.", d.getDeviceName()))
+ onu.startKeepAlive()
+ vendorID = "FEC"
+ VendorSpecific = "EponONU"
+ //serialNumber = macAddress[6:12]
+ serialNumber = strings.Replace(macAddress, ":", "", -1)[6:12]
+
+ onuDiscInd := &oop.OnuDiscIndication{
+ IntfId: 0,
+ SerialNumber: &oop.SerialNumber{
+ VendorId: []byte(vendorID),
+ VendorSpecific: []byte(VendorSpecific),
+ },
+ }
+ if err = d.Base.DH.onuDiscIndication2(ctx, onuDiscInd, serialNumber, macAddress); err != nil {
+ return
+ }
+
+ onu.setObjectContext(oc)
+ onu.updateMap()
+ // onu := FindL2oamDevice(macAddress)
+ // if onu == nil {
+ // logger.Debug(ctx, fmt.Sprintf("[%s] addOnu() NewL2oamOnuDevice() called. macAddress=%s", d.getDeviceName(), macAddress))
+ // onu, _ = NewL2oamOnuDevice(macAddress, d.Base.DH, deviceId)
+ // onu.startKeepAlive()
+ // }
+ // onu.setActiveState(ctx, true)
+ device = FindL2oamDevice(macAddress)
+ onuInd := &oop.OnuIndication{
+ IntfId: 0,
+ OnuId: device.(*L2oamOnuDevice).OnuID,
+ OperState: "up",
+ AdminState: "up",
+ SerialNumber: onuDiscInd.SerialNumber,
+ }
+ if err := d.Base.DH.onuIndication(ctx, onuInd); err != nil {
+ return
+ }
+ }()
+}
+
+func (dh *DeviceHandler) fakeOmciIndication(ctx context.Context, intfID uint32, onuID uint32, pkt []byte) error {
+ omciInd := &oop.OmciIndication{
+ IntfId: intfID,
+ OnuId: onuID,
+ Pkt: make([]byte, 48),
+ }
+
+ onuKey := dh.formOnuKey(intfID, onuID)
+ onuDev, ok := dh.onus.Load(onuKey)
+ if !ok {
+ logger.Errorf(ctx, "not-found-onu-device. intf-id:%d, onu-id:%d", intfID, onuID)
+ return nil
+ }
+ onuDeviceID := (onuDev.(*OnuDevice)).deviceID
+ onu := FindL2oamDeviceByDeviceID(onuDeviceID)
+ if onu == nil {
+ logger.Errorf(ctx, "not-found-l2oam-onu-device. onu-device-id:%s", onuDeviceID)
+ return nil
+ }
+
+
+ if len(pkt) < 11 {
+ return nil
+ }
+ messageType := uint8(pkt[2])
+ meID := binary.BigEndian.Uint16(pkt[4:6])
+ meSubID := binary.BigEndian.Uint16(pkt[6:8])
+ omciInd.Pkt[0] = pkt[0]
+ omciInd.Pkt[1] = pkt[1]
+ omciInd.Pkt[2] = (pkt[2] & 0xbf) | 0x20 // AR -> AK
+ copy(omciInd.Pkt[3:8], pkt[3:8])
+ contentIdx := 8
+
+ switch messageType {
+ case 0x49: // Get
+ if meID == 0x0101 { // ONU2-G
+ if pkt[contentIdx] == 0xe0 {
+ copy(omciInd.Pkt[contentIdx:], []byte{
+ 0x00, 0xe0, 0x00, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x31, 0x32, 0x33, 0x34, 0x35, 0xb4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ })
+ } else if pkt[contentIdx] == 0x80 {
+ copy(omciInd.Pkt[contentIdx:], []byte{
+ 0x00, 0x80, 0x00, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x31, 0x32, 0x33, 0x34, 0x35, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ })
+ } else {
+ logger.Error(ctx, fmt.Sprintf("fakeOmciIndication() unknown-format:message-type:%x, me-id:%x",
+ messageType, meID))
+ }
+ } else if meID == 0x0100 { // ONU-G
+ copy(omciInd.Pkt[contentIdx:], []byte{
+ 0x00, 0xa0, 0x00, 0x45, 0x50, 0x4f, 0x4e, 0x45,
+ 0x50, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ })
+ } else if meID == 0x0007 { // Software image
+ if meSubID == 0 {
+ copy(omciInd.Pkt[contentIdx:], []byte{
+ 0x00, 0xa0, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x31, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ })
+ } else {
+ copy(omciInd.Pkt[contentIdx:], []byte{
+ 0x00, 0xa0, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x31, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ })
+ }
+ } else if meID == 0x0086 { // IP host config data
+ var macOctets [6]byte
+ onuDev := onu.(*L2oamOnuDevice)
+ if len(onuDev.MacAddress) == 12 {
+ for idx := 0; idx < 6; idx++ {
+ if v, err := strconv.ParseInt(onuDev.MacAddress[idx*2:(idx*2)+2], 16, 8); err == nil {
+ macOctets[idx] = byte(v)
+ }
+ }
+ }
+ // 0x09-0a: Mask
+ // 0x0b-10: MAC Address
+ copy(omciInd.Pkt[contentIdx:], []byte{
+ 0x00, 0x40, 0x00, macOctets[0], macOctets[1], macOctets[2], macOctets[3], macOctets[4],
+ macOctets[5], 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ })
+ } else {
+ logger.Error(ctx, fmt.Sprintf("fakeOmciIndication() unknown-format:message-type:%x, me-id:%x",
+ messageType, meID))
+ }
+ case 0x4f: // MIB reset
+ copy(omciInd.Pkt[contentIdx:], []byte{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ })
+ case 0x4d: // MIB upload
+ copy(omciInd.Pkt[contentIdx:], []byte{
+ // first 2 bytes: the number of upload message
+ // upload messages are sent specified number of times
+ // following this message
+ 0x01, 0x23 - 3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ })
+ case 0x4e: // MIB upload next
+ uploadIdx := binary.BigEndian.Uint16(pkt[contentIdx : contentIdx+2])
+ copy(omciInd.Pkt[contentIdx:], [][]byte{
+ {
+ 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ // addr: memo
+ // 0x08-09: Class ID(#6 Circuit Pack)
+ // 0x0a-0b: Managed entity ID(#257 ONU2-G)
+ // 0x0c-0d: field mask, 0xf000 means first 4 fields
+ // are stored in this message
+ // 0x0e: Type, 0x2f means 10/100/1000 BASE-T
+ // 0x0f: Number of ports
+ // 0x10-17: Serial Number
+ // 0x18-25: Version
+ 0x00, 0x06, 0x01, 0x01, 0xf0, 0x00, 0x2f, 0x04, // 0x08 - 0x0f
+ 0x49, 0x53, 0x4b, 0x54, 0x71, 0xe8, 0x00, 0x80, // 0x10 - 0x17
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18 - 0x1f
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, // 0x20 - 0x27
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28 - 0x2f
+ },
+ {
+ // addr: memo
+ // 0x08-09: Class ID(#6 Circuit Pack)
+ // 0x0a-0b: Managed entity ID(#257 ONU2-G)
+ // 0x0c-0d: field mask, 0x0f00 means from 5th to 8th fields
+ // are stored in this message
+ // 0x0e-11: Vendor ID
+ // 0x12: Administrative state
+ // 0x13: Operational state
+ // 0x14: Bridged or IP ind
+ 0x00, 0x06, 0x01, 0x01, 0x0f, 0x00, 0x42, 0x52, // 0x08 - 0x0f
+ 0x43, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 - 0x17
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18 - 0x1f
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 - 0x27
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28 - 0x2f
+ },
+ {
+ // addr: memo
+ // 0x08-09: Class ID(#6 Circuit Pack)
+ // 0x0a-0b: Managed entity ID(#257 ONU2-G)
+ // 0x0c-0d: field mask, 0x00f8 means from 9th to 13th fields
+ // are stored in this message
+ // 0x0e-21: Equipment ID
+ // 0x22: Card configuration
+ // 0x23: Total T-CONT buffer number
+ // 0x24: Total priority queue number
+ // 0x25: Total traffic scheduler number
+ 0x00, 0x06, 0x01, 0x01, 0x00, 0xf8, 0x20, 0x20, // 0x08 - 0x0f
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0x10 - 0x17
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0x18 - 0x1f
+ 0x20, 0x20, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // 0x20 - 0x27
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28 - 0x2f
+ },
+ {
+ // addr: memo
+ // 0x08-09: Class ID(#6 Circuit Pack)
+ // 0x0a-0b: Managed entity ID(#257 ONU2-G)
+ // 0x0c-0d: field mask, 0x0004 means from 14th field
+ // is stored in this message
+ // 0x0e-0x11: Power shed override
+ 0x00, 0x06, 0x01, 0x01, 0x00, 0x04, 0x00, 0x00, // 0x08 - 0x0f
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 - 0x17
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18 - 0x1f
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 - 0x27
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28 - 0x2f
+ },
+ {
+ // addr: memo
+ // 0x08-09: Class ID(#6 Circuit Pack)
+ // 0x0a-0b: Managed entity ID(#384)
+ // 0x0c-0d: field mask, 0xf000 means first 4 fields
+ // are stored in this message
+ // 0x0e: Type, 0xee means XG-PON10G10
+ // 0x0f: Number of ports
+ // 0x10-17: Serial Number
+ // 0x18-25: Version
+ 0x00, 0x06, 0x01, 0x80, 0xf0, 0x00, 0xee, 0x01,
+ 0x49, 0x53, 0x4b, 0x54, 0x71, 0xe8, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Circuit Pack-#384
+ 0x00, 0x06, 0x01, 0x80, 0x0f, 0x00, 0x42, 0x52,
+ 0x43, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Circuit Pack-#384
+ 0x00, 0x06, 0x01, 0x80, 0x00, 0xf8, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x08, 0x40, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Circuit Pack-#384
+ 0x00, 0x06, 0x01, 0x80, 0x00, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Physical Path Termination Point Ethernet UNI(port#1)
+ // addr: memo
+ // 0x08-09: Class ID(#11 Physical Path Termination Point Ethernet UNI)
+ // 0x0a: slot ID
+ // 0x0b: port ID
+ // 0x0c-0d: field mask, 0xfffe means first 15 fields
+ // are stored in this message
+ // 0x0e: Expected type
+ // 0x0f: Sensed type, 0x2f means 10/100/1000 BASE-T
+ // 0x10: Auto detection configuration, 0x00 means Rate/Duplex = Auto/Auto
+ // 0x11: Ethernet loopback configulation
+ // 0x12: Administrative state(0: unlocks, 1: locks)
+ // 0x13: Operational state(0: enabled, 1: disabled)
+ // 0x14: Configuration ind, 0x03 means Gigabit Ethernet full duplex
+ // 0x15-16: Max frame size
+ // 0x17: DTE or DCE ind, 0x00 means DCE or MDI-X
+ // 0x18-19: Pause time
+ // 0x1a: Bridged or IP ind, 0x02 means Depends on the parent circuit pack
+ // 0x1b: ARC
+ // 0x1c: ARC interval
+ // 0x1d: PPPoE filter
+ // 0x1e: Power control
+ 0x00, 0x0b, 0x01, 0x01, 0xff, 0xfe, 0x00, 0x2f, // 0x08 - 0x0f
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0xee, 0x00, // 0x10 - 0x17
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18 - 0x1f
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 - 0x27
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28 - 0x2f
+ },
+ { // Physical Path Termination Point Ethernet UNI(port#2)
+ 0x00, 0x0b, 0x01, 0x02, 0xff, 0xfe, 0x00, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0xee, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Physical Path Termination Point Ethernet UNI(port#3)
+ 0x00, 0x0b, 0x01, 0x03, 0xff, 0xfe, 0x00, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0xee, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Physical Path Termination Point Ethernet UNI(port#4)
+ 0x00, 0x0b, 0x01, 0x04, 0xff, 0xfe, 0x00, 0x2f,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0xee, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // T-CONT(T-CONT#1)
+ // addr: memo
+ // 0x08-09: Class ID(#262 T-CONT)
+ // 0x0a: slot ID
+ // 0x0b: T-CONT ID
+ // 0x0c-0d: field mask, 0xe000 means first 3 fields
+ // are stored in this message
+ // 0x0e-0f: Alloc-ID
+ // 0x10: Deprecated
+ // 0x11: Policy
+ 0x01, 0x06, 0x80, 0x01, 0xe0, 0x00, 0xff, 0xff, // 0x08 - 0x0f
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 - 0x17
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // T-CONT(T-CONT#2)
+ 0x01, 0x06, 0x80, 0x02, 0xe0, 0x00, 0xff, 0xff,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // T-CONT(T-CONT#3)
+ 0x01, 0x06, 0x80, 0x03, 0xe0, 0x00, 0xff, 0xff,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // T-CONT(T-CONT#4)
+ 0x01, 0x06, 0x80, 0x04, 0xe0, 0x00, 0xff, 0xff,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // T-CONT(T-CONT#5)
+ 0x01, 0x06, 0x80, 0x05, 0xe0, 0x00, 0xff, 0xff,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // T-CONT(T-CONT#6)
+ 0x01, 0x06, 0x80, 0x06, 0xe0, 0x00, 0xff, 0xff,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // T-CONT(T-CONT#7)
+ 0x01, 0x06, 0x80, 0x07, 0xe0, 0x00, 0xff, 0xff,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // T-CONT(T-CONT#8)
+ 0x01, 0x06, 0x80, 0x08, 0xe0, 0x00, 0xff, 0xff,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // ANI-G(port#1)
+ // addr: memo
+ // 0x08-09: Class ID(#263 ANI-G)
+ // 0x0a: slot ID
+ // 0x0b: port ID
+ // 0x0c-0d: field mask, 0xffff means first 16 fields
+ // are stored in this message
+ // 0x0e: SR indication
+ // 0x0f-10: Total T-CONT number
+ // 0x11-12: GEM block length
+ // 0x13: Piggyback DBA reporting
+ // 0x14: Deprecated
+ // 0x15: SF threshold
+ // 0x16: SD threshold
+ // 0x17: ARC
+ // 0x18: ARC interval
+ // 0x19-0x1a: Optical signal level
+ // 0x1b: Lower optical threshold
+ // 0x1c: Upper optical threshold
+ // 0x1d-0x1e: ONU response time
+ // 0x1f-0x20: Transmit optical level
+ // 0x21: Lower transmit power threshold
+ // 0x22: Upper transmit power threshold
+ 0x01, 0x07, 0x80, 0x01, 0xff, 0xff, 0x01, 0x00, // 0x08 - 0x0f
+ 0x08, 0x00, 0x30, 0x00, 0x00, 0x05, 0x09, 0x00, // 0x10 - 0x17
+ 0x00, 0xe0, 0x54, 0xff, 0xff, 0x00, 0x00, 0x0c, // 0x18 - 0x1f
+ 0x63, 0x81, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 - 0x27
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28 - 0x2f
+ },
+ { // UNI-G(port#1)
+ // addr: memo
+ // 0x08-09: Class ID(#264 UNI-G)
+ // 0x0a-0b: Managed entity ID
+ // 0x0c-0d: field mask, 0xf800 means first 5 fields
+ // are stored in this message
+ // 0x0e-0f: Deprecated
+ // 0x10: Administrative state(0: unlocks, 1: locks)
+ // 0x11: Management capability
+ // 0x12-13: Non-OMCI management identifier
+ // 0x14-15: Relay agent options
+ 0x01, 0x08, 0x01, 0x01, 0xf8, 0x00, 0x00, 0x00, // 0x08 - 0x0f
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 - 0x17
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18 - 0x1f
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 - 0x27
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28 - 0x2f
+ },
+ /*
+ { // UNI-G(port#2)
+ 0x01, 0x08, 0x01, 0x02, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // UNI-G(port#3)
+ 0x01, 0x08, 0x01, 0x03, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // UNI-G(port#4)
+ 0x01, 0x08, 0x01, 0x04, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ */
+ { // Priority Queue(downstream-queue#1)
+ // addr: memo
+ // 0x08-09: Class ID(#277 Priority Queue)
+ // 0x0a-0b: Managed entity ID(most significant bit represents the direction (1: upstream, 0: downstream))
+ // 0x0c-0d: field mask, 0x000f means from 13th to 16th fields
+ // are stored in this message
+ // 0x0e-0f: Deprecated
+ 0x01, 0x15, 0x00, 0x01, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#1)
+ 0x01, 0x15, 0x00, 0x01, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#1)
+ 0x01, 0x15, 0x80, 0x01, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#1)
+ 0x01, 0x15, 0x80, 0x01, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x00, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#2)
+ 0x01, 0x15, 0x00, 0x02, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#2)
+ 0x01, 0x15, 0x00, 0x02, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#2)
+ 0x01, 0x15, 0x80, 0x02, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#2)
+ 0x01, 0x15, 0x80, 0x02, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x01, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#3)
+ 0x01, 0x15, 0x00, 0x03, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#3)
+ 0x01, 0x15, 0x00, 0x03, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x02, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#3)
+ 0x01, 0x15, 0x80, 0x03, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#3)
+ 0x01, 0x15, 0x80, 0x03, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x02, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#4)
+ 0x01, 0x15, 0x00, 0x04, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#4)
+ 0x01, 0x15, 0x00, 0x04, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x03, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#4)
+ 0x01, 0x15, 0x80, 0x04, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#4)
+ 0x01, 0x15, 0x80, 0x04, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x03, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#5)
+ 0x01, 0x15, 0x00, 0x05, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#5)
+ 0x01, 0x15, 0x00, 0x05, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x04, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#5)
+ 0x01, 0x15, 0x80, 0x05, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#5)
+ 0x01, 0x15, 0x80, 0x05, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x04, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#6)
+ 0x01, 0x15, 0x00, 0x06, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#6)
+ 0x01, 0x15, 0x00, 0x06, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x05, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#6)
+ 0x01, 0x15, 0x80, 0x06, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#6)
+ 0x01, 0x15, 0x80, 0x06, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x05, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#7)
+ 0x01, 0x15, 0x00, 0x07, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#7)
+ 0x01, 0x15, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x06, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#7)
+ 0x01, 0x15, 0x80, 0x07, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#7)
+ 0x01, 0x15, 0x80, 0x07, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x06, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#8)
+ 0x01, 0x15, 0x00, 0x08, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#8)
+ 0x01, 0x15, 0x00, 0x08, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x07, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#8)
+ 0x01, 0x15, 0x80, 0x08, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#8)
+ 0x01, 0x15, 0x80, 0x08, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x07, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#9)
+ 0x01, 0x15, 0x00, 0x09, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#9)
+ 0x01, 0x15, 0x00, 0x09, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#9)
+ 0x01, 0x15, 0x80, 0x09, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#9)
+ 0x01, 0x15, 0x80, 0x09, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0x00, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#10)
+ 0x01, 0x15, 0x00, 0x0a, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#10)
+ 0x01, 0x15, 0x00, 0x0a, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x01, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#10)
+ 0x01, 0x15, 0x80, 0x0a, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#10)
+ 0x01, 0x15, 0x80, 0x0a, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0x01, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#11)
+ 0x01, 0x15, 0x00, 0x0b, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#11)
+ 0x01, 0x15, 0x00, 0x0b, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x02, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#11)
+ 0x01, 0x15, 0x80, 0x0b, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#11)
+ 0x01, 0x15, 0x80, 0x0b, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0x02, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#12)
+ 0x01, 0x15, 0x00, 0x0c, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#12)
+ 0x01, 0x15, 0x00, 0x0c, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x03, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#12)
+ 0x01, 0x15, 0x80, 0x0c, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#12)
+ 0x01, 0x15, 0x80, 0x0c, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0x03, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#13)
+ 0x01, 0x15, 0x00, 0x0d, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#13)
+ 0x01, 0x15, 0x00, 0x0d, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x04, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#13)
+ 0x01, 0x15, 0x80, 0x0d, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#13)
+ 0x01, 0x15, 0x80, 0x0d, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0x04, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#14)
+ 0x01, 0x15, 0x00, 0x0e, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#14)
+ 0x01, 0x15, 0x00, 0x0e, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x05, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#14)
+ 0x01, 0x15, 0x80, 0x0e, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#14)
+ 0x01, 0x15, 0x80, 0x0e, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0x05, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#15)
+ 0x01, 0x15, 0x00, 0x0f, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#15)
+ 0x01, 0x15, 0x00, 0x0f, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x06, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#15)
+ 0x01, 0x15, 0x80, 0x0f, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#15)
+ 0x01, 0x15, 0x80, 0x0f, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0x06, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#16)
+ 0x01, 0x15, 0x00, 0x10, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(downstream-queue#16)
+ 0x01, 0x15, 0x00, 0x10, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x00, 0x07, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(upstream-queue#16)
+ 0x01, 0x15, 0x80, 0x10, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Priority Queue(uptream-queue#16)
+ 0x01, 0x15, 0x80, 0x10, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0x07, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x11, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x11, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x03, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x11, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x11, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x03, 0x00, 0x00, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x12, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x12, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x03, 0x00, 0x01, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x12, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x12, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x03, 0x00, 0x01, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x13, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x13, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x03, 0x00, 0x02, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x13, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x13, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x03, 0x00, 0x02, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x14, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x14, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x03, 0x00, 0x03, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x14, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x14, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x03, 0x00, 0x03, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x15, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x15, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x03, 0x00, 0x04, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x15, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x15, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x03, 0x00, 0x04, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x16, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x16, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x03, 0x00, 0x05, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x16, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x16, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x03, 0x00, 0x05, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x17, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x17, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x03, 0x00, 0x06, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x17, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x17, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x03, 0x00, 0x06, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x18, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x18, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x03, 0x00, 0x07, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x18, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x18, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x03, 0x00, 0x07, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x19, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x19, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x19, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x19, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x04, 0x00, 0x00, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1a, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1a, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0x01, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1a, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1a, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x04, 0x00, 0x01, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1b, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1b, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0x02, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1b, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1b, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x04, 0x00, 0x02, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1c, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1c, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0x03, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1c, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1c, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x04, 0x00, 0x03, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1d, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1d, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0x04, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1d, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1d, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x04, 0x00, 0x04, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1e, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1e, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0x05, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1e, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1e, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x04, 0x00, 0x05, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1f, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x1f, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0x06, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1f, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x1f, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x04, 0x00, 0x06, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x20, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x20, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0x07, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x20, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x20, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x04, 0x00, 0x07, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x21, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x21, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x05, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x21, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x21, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x05, 0x00, 0x00, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x22, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x22, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x05, 0x00, 0x01, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x22, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x22, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x05, 0x00, 0x01, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x23, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x23, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x05, 0x00, 0x02, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x23, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x23, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x05, 0x00, 0x02, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x24, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x24, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x05, 0x00, 0x03, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x24, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x24, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x05, 0x00, 0x03, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x25, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x25, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x05, 0x00, 0x04, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x25, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x25, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x05, 0x00, 0x04, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x26, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x26, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x05, 0x00, 0x05, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x26, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x26, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x05, 0x00, 0x05, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x27, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x27, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x05, 0x00, 0x06, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x27, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x27, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x05, 0x00, 0x06, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x28, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x28, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x05, 0x00, 0x07, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x28, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x28, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x05, 0x00, 0x07, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x29, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x29, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x06, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x29, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x29, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x06, 0x00, 0x00, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2a, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2a, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x06, 0x00, 0x01, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2a, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2a, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x06, 0x00, 0x01, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2b, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2b, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x06, 0x00, 0x02, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2b, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2b, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x06, 0x00, 0x02, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2c, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2c, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x06, 0x00, 0x03, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2c, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2c, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x06, 0x00, 0x03, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2d, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2d, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x06, 0x00, 0x04, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2d, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2d, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x06, 0x00, 0x04, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2e, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2e, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x06, 0x00, 0x05, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2e, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2e, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x06, 0x00, 0x05, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2f, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x2f, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x06, 0x00, 0x06, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2f, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x2f, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x06, 0x00, 0x06, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x30, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x30, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x06, 0x00, 0x07, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x30, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x30, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x06, 0x00, 0x07, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x31, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x31, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x07, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x31, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x31, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x07, 0x00, 0x00, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x32, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x32, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x07, 0x00, 0x01, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x32, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x32, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x07, 0x00, 0x01, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x33, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x33, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x07, 0x00, 0x02, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x33, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x33, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x07, 0x00, 0x02, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x34, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x34, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x07, 0x00, 0x03, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x34, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x34, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x07, 0x00, 0x03, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x35, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x35, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x07, 0x00, 0x04, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x35, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x35, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x07, 0x00, 0x04, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x36, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x36, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x07, 0x00, 0x05, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x36, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x36, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x07, 0x00, 0x05, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x37, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x37, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x07, 0x00, 0x06, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x37, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x37, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x07, 0x00, 0x06, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x38, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x38, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x07, 0x00, 0x07, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x38, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x38, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x07, 0x00, 0x07, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x39, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x39, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x39, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x39, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x08, 0x00, 0x00, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3a, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3a, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x01, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3a, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3a, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x08, 0x00, 0x01, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3b, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3b, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x02, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3b, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3b, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x08, 0x00, 0x02, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3c, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3c, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x03, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3c, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3c, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x08, 0x00, 0x03, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3d, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3d, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x04, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3d, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3d, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x08, 0x00, 0x04, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3e, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3e, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x05, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3e, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3e, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x08, 0x00, 0x05, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3f, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x3f, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x06, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3f, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x3f, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x08, 0x00, 0x06, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x40, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x00, 0x40, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x07, 0x01, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x40, 0x00, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x01, 0x15, 0x80, 0x40, 0xff, 0xf0, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x08, 0x00, 0x07, 0x80, 0x08, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Traffic Scheduler
+ 0x01, 0x16, 0x80, 0x00, 0xf0, 0x00, 0x80, 0x08,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Traffic Scheduler
+ 0x01, 0x16, 0x80, 0x00, 0xf0, 0x00, 0x80, 0x09,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Traffic Scheduler
+ 0x01, 0x16, 0x80, 0x00, 0xf0, 0x00, 0x80, 0x0a,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Traffic Scheduler
+ 0x01, 0x16, 0x80, 0x00, 0xf0, 0x00, 0x80, 0x0b,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Traffic Scheduler
+ 0x01, 0x16, 0x80, 0x00, 0xf0, 0x00, 0x80, 0x0c,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Traffic Scheduler
+ 0x01, 0x16, 0x80, 0x00, 0xf0, 0x00, 0x80, 0x0d,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Traffic Scheduler
+ 0x01, 0x16, 0x80, 0x00, 0xf0, 0x00, 0x80, 0x0e,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // Traffic Scheduler
+ 0x01, 0x16, 0x80, 0x00, 0xf0, 0x00, 0x80, 0x0f,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { // ONU2-G
+ 0x01, 0x01, 0x00, 0x00, 0x07, 0xfc, 0x00, 0x40,
+ 0x08, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7f, 0x00, 0x00, 0x3f, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ }[uploadIdx][:])
+ case 0x44: // Create
+ // meID
+ // 0x0110: GAL Ethernet profile
+ // 0x002d: MAC bridge service profile
+ // 0x002f: MAC brige port configuration data
+ // 0x00ab: Extended VLAN tagging operation configuration data
+ // 0x0082: IEEE 802.1p mapper service profile
+ // 0x010c: GEM port network CTP
+ // 0x010a: GEM interworking termination point
+ copy(omciInd.Pkt[contentIdx:], []byte{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ })
+ case 0x48: // Set
+ // meID
+ // 0x0101: ONU2-G
+ // 0x0100: ONU-G
+ // 0x0108: UNI-G
+ // 0x0106: T-CONT
+ // 0x0115: Priority queue
+ // 0x0082: IEEE 802.1p mapper service profile
+ copy(omciInd.Pkt[contentIdx:], []byte{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ })
+ }
+ go func() {
+ if err := dh.omciIndication(ctx, omciInd); err != nil {
+ return
+ }
+ }()
+
+ return nil
+}
+
+func (d *L2oamOltDevice) setActiveState(ctx context.Context, isActive bool) {
+ d.Base.setActiveState(ctx, isActive)
+}
+func (d *L2oamOltDevice) startMountSequence(ctx context.Context, pkgType string, cmd *L2oamCmd) {
+}
+func (d *L2oamOltDevice) reboot(ctx context.Context) {
+}
+func (d *L2oamOltDevice) updateMap() {
+ mapMutex.Lock()
+ defer mapMutex.Unlock()
+ _, ok := deviceMap[d.Base.DstMac]
+ if ok {
+ deviceMap[d.Base.DstMac] = d
+ }
+}
+func (d *L2oamOltDevice) setL2oamCmd(cmd *L2oamCmd) {
+ d.L2oamCmd = cmd
+}
+func (d *L2oamOltDevice) getL2oamCmd() *L2oamCmd {
+ return d.L2oamCmd
+}
+func (d *L2oamOltDevice) setObjectContext(oc *l2oam.TomiObjectContext) {
+ d.ObjectContext = oc
+}
+func (d *L2oamOltDevice) getObjectContext() *l2oam.TomiObjectContext {
+ return d.ObjectContext
+}
+func (d *L2oamOltDevice) setReferenceTable(tbl *l2oam.GetTrafficControlReferenceTableRes) {
+ d.ReferenceTable = tbl
+}
+func (d *L2oamOltDevice) getReferenceTable() *l2oam.GetTrafficControlReferenceTableRes {
+ return d.ReferenceTable
+}
+func (d *L2oamOltDevice) setProfile(down *l2oam.SetGenericActionCreateRes, up *l2oam.SetGenericActionCreateRes) {
+ d.DownProfile = down
+ d.UpProfile = up
+}
+func (d *L2oamOltDevice) getProfile() (*l2oam.SetGenericActionCreateRes, *l2oam.SetGenericActionCreateRes) {
+ return d.DownProfile, d.UpProfile
+}
+func (d *L2oamOltDevice) delete(ctx context.Context) {
+ d.Base.delete(ctx)
+}
+func (d *L2oamOltDevice) setDeleteFlag() {
+ d.Base.setDeleteFlag()
+}
+func (d *L2oamOltDevice) getDeleteFlag() bool {
+ return d.Base.getDeleteFlag()
+}
+func (d *L2oamOltDevice) setAutonomousFlag(flag bool) {
+ d.Base.setAutonomousFlag(flag)
+ d.updateMap()
+}
+func (d *L2oamOltDevice) getAutonomousFlag() bool {
+ return d.Base.getAutonomousFlag()
+}
+func (d *L2oamOltDevice) receiveEapol(etherPacket *layers.Ethernet) {
+ d.Base.receiveEapol(etherPacket)
+}
+
+func (d *L2oamOltDevice) addFlow(ctx context.Context, cmd *L2oamCmd) error {
+ d.autonomousExecMutex.Lock()
+ defer d.autonomousExecMutex.Unlock()
+
+ onu := FindL2oamDeviceByDeviceID(cmd.OnuDeviceID)
+ if onu == nil {
+ return fmt.Errorf(fmt.Sprintf("catnnot-get-onu-device. device-id:%s", cmd.OnuDeviceID))
+ } else if !(onu.(*L2oamOnuDevice)).Base.isActive() {
+ logger.Debug(ctx, fmt.Sprintf("onu is not active. device-id:%s", cmd.OnuDeviceID))
+ } else {
+ logger.Debug(ctx, fmt.Sprintf("add-flow-for-onu. device-id:%s", cmd.OnuDeviceID))
+
+ oc := onu.getObjectContext()
+ if oc == nil {
+ logger.Error(ctx, fmt.Sprintf("catnnot-get-onu-object-context. device-id:%s", cmd.OnuDeviceID))
+ } else {
+ getTrafficControl := onu.getReferenceTable()
+ if getTrafficControl == nil {
+ getTrafficControl = sendGetTrafficControlReferenceTable(ctx, d, oc)
+ if getTrafficControl == nil {
+ return errors.New("L2oamAddFlow() sendGetTrafficControlReferenceTable() error. ")
+ }
+ }
+ downProfile, upProfile := onu.getProfile()
+ // if profiles are created, remove these profiles.
+ if downProfile != nil {
+ if err := sendSetActionDelete(ctx, d, downProfile.GetTrafficProfile(), l2oam.ActionTypeTrafficProfile); err != nil {
+ logger.Error(ctx,
+ fmt.Sprintf("L2oamAddFlow() Generic/Action Delete(downstream) error. %v", err))
+ }
+ }
+ if upProfile != nil {
+ if err := sendSetActionDelete(ctx, d, upProfile.GetTrafficProfile(), l2oam.ActionTypeTrafficProfile); err != nil {
+ logger.Error(ctx,
+ fmt.Sprintf("L2oamAddFlow() Generic/Action Delete(upstream) error. %v", err))
+ }
+ }
+ downProfile = sendSetGenericActionCreate(ctx, d, l2oam.ActionTypeTrafficProfile)
+ if downProfile == nil {
+ return errors.New("L2oamAddFlow() Generic/Action Create(downstream) error")
+ }
+ upProfile = sendSetGenericActionCreate(ctx, d, l2oam.ActionTypeTrafficProfile)
+ if upProfile == nil {
+ return errors.New("L2oamAddFlow() Generic/Action Create(upstream) error")
+ }
+ onu.setReferenceTable(getTrafficControl)
+ onu.setProfile(downProfile, upProfile)
+ onu.updateMap()
+
+ err01 := sendSetTrafficControl(ctx, d, getTrafficControl.GetReferenceControlDown(), downProfile.GetTrafficProfile())
+ err02 := sendSetTrafficControl(ctx, d, getTrafficControl.GetReferenceControlUp(), upProfile.GetTrafficProfile())
+ logger.Debug(ctx, fmt.Sprintf("traffic-control/traffic-profile results: %v, %v", err01, err02))
+
+ err03 := sendSetPriority(ctx, d, downProfile.GetTrafficProfile())
+ err04 := sendSetGuranteedRate(ctx, d, cmd.Cir, downProfile.GetTrafficProfile())
+ err05 := sendSetGuranteedRate(ctx, d, cmd.Cir, upProfile.GetTrafficProfile())
+ err06 := sendSetBestEffortRate(ctx, d, cmd.Pir, downProfile.GetTrafficProfile())
+ err07 := sendSetBestEffortRate(ctx, d, cmd.Pir, upProfile.GetTrafficProfile())
+ logger.Debug(ctx, fmt.Sprintf("traffic-profile-settings-results: %v, %v, %v, %v, %v",
+ err03, err04, err05, err06, err07))
+ }
+ }
+
+ return nil
+}
+
+func (d *L2oamOltDevice) updateFlow(ctx context.Context, cmd *L2oamCmd) error {
+ d.autonomousExecMutex.Lock()
+ defer d.autonomousExecMutex.Unlock()
+
+ onuDeviceID := cmd.OnuDeviceID
+ logger.Debugf(ctx, "updateFlow() Start. onu-device-id:%s, tpid:%x, vid%x",
+ onuDeviceID, cmd.Tpid, cmd.Vid)
+
+ onu := FindL2oamDeviceByDeviceID(onuDeviceID)
+ if onu == nil {
+ return errors.New("L2oamUpdateFlow() FindL2oamDeviceByDeviceId() error. onu not found")
+ }
+ var action *l2oam.TomiObjectContext
+ var err error
+ onuID := onu.getObjectContext()
+ for _, settings := range d.L2SwitchDomainMap {
+ if _, exists := settings.Onus[onuDeviceID]; exists {
+ if err = sendSetActionInletEntryUsDel(ctx,
+ d, settings.OcAction, settings.Tpid, settings.Vid, onuID); err != nil {
+ logger.Errorf(ctx, "error:%v", err)
+ }
+ delete(settings.Onus, onuDeviceID)
+ }
+ }
+ vidValue := byte2Int(cmd.Vid)
+ settings, exists := d.L2SwitchDomainMap[vidValue]
+ if !exists {
+ action, err = sendSetGenericActionCreateForDS(ctx, d)
+ if err != nil {
+ logger.Errorf(ctx, "error:%v", err)
+ }
+ settings = &l2SwitchDomain{
+ OcAction: action,
+ Tpid: cmd.Tpid,
+ Vid: cmd.Vid,
+ Onus: map[string]bool{},
+ }
+ d.L2SwitchDomainMap[vidValue] = settings
+ if err := sendSetL2SwitchingDomainForDS(ctx, d, action, cmd.Tpid, cmd.Vid); err != nil {
+ logger.Errorf(ctx, "error:%v", err)
+ }
+ } else {
+ action = settings.OcAction
+ }
+ if err := sendSetDefaultOutlet(ctx, d, action, onuID); err != nil {
+ logger.Errorf(ctx, "error:%v", err)
+ }
+ if err := sendSetL2SwitchingDomainForUS(ctx, d, action, cmd.Tpid, cmd.Vid, onuID); err != nil {
+ logger.Errorf(ctx, "error:%v", err)
+ }
+ settings.Onus[onuDeviceID] = true
+
+ logger.Debugf(ctx, "updateFlow() end. onu-device-id:%s, tpid:%x, vid%x",
+ onuDeviceID, cmd.Tpid, cmd.Vid)
+
+ return nil
+}
+
+func (d *L2oamOltDevice) removeFlow(ctx context.Context) error {
+ logger.Debugf(ctx, "removeFlow() Start. device-id:%s", d.Base.DeviceID)
+
+ for _, settings := range d.L2SwitchDomainMap {
+ for onuDeviceID := range settings.Onus {
+ onu := FindL2oamDeviceByDeviceID(onuDeviceID)
+ if onu == nil {
+ logger.Errorf(ctx, "onu not found. device-id:%s", onuDeviceID)
+ continue
+ }
+ onuID := onu.getObjectContext()
+ if err := sendSetActionInletEntryUsDel(ctx,
+ d, settings.OcAction, settings.Tpid, settings.Vid, onuID); err != nil {
+ logger.Errorf(ctx, "error:%v", err)
+ }
+ }
+ settings.Onus = map[string]bool{}
+
+ if err := sendSetActionInletEntryDsDel(ctx, d, settings.OcAction, settings.Tpid, settings.Vid); err != nil {
+ logger.Errorf(ctx, "error:%v", err)
+ }
+ if err := sendSetActionDeleteStream(ctx, d, settings.OcAction); err != nil {
+ logger.Errorf(ctx, "error:%v", err)
+ }
+ }
+ d.L2SwitchDomainMap = map[uint32]*l2SwitchDomain{}
+
+ return nil
+}
+
+// L2oamOnuDevice =================================
+// L2oamOnuDevice provides functions for OLT device
+// ================================================
+type L2oamOnuDevice struct {
+ Base DeviceBase
+ OnuID uint32
+ ObjectContext *l2oam.TomiObjectContext
+ ReferenceTable *l2oam.GetTrafficControlReferenceTableRes
+ DownProfile *l2oam.SetGenericActionCreateRes
+ UpProfile *l2oam.SetGenericActionCreateRes
+ PkgType string
+ keepAliveResponse chan string
+ MacAddress string
+}
+
+func (d *L2oamOnuDevice) getDeviceName() string {
+ return d.Base.getDeviceName()
+}
+func (d *L2oamOnuDevice) send(message gopacket.SerializableLayer) error {
+ return d.Base.send(message)
+}
+func (d *L2oamOnuDevice) waitResponse(timeoutSecond int) (*layers.Ethernet, error) {
+ return d.Base.waitResponse(timeoutSecond)
+}
+func (d *L2oamOnuDevice) sendAndWait(message gopacket.SerializableLayer, timeoutSecond int) (*layers.Ethernet, error) {
+ return d.Base.sendAndWait(message, timeoutSecond)
+}
+func (d *L2oamOnuDevice) recieve(etherPacket *layers.Ethernet) {
+ d.Base.recieve(etherPacket)
+}
+func (d *L2oamOnuDevice) recieveKeepAlive(etherPacket *layers.Ethernet) {
+ packet := &l2oam.OAMPDUInformation{}
+ if err := packet.Decode(etherPacket.Payload); err != nil {
+ return
+ }
+ if (l2oam.OnuPkgType == l2oam.OnuPkgTypeA) && (packet.IsOnuPkgA()) {
+ logger.Info(context.Background(), fmt.Sprintf("[%s] recieveKeepAlive() Pkg type A.", d.getDeviceName()))
+ if d.keepAliveResponse != nil {
+ d.keepAliveResponse <- "PkgTypeError"
+ }
+ // d.Base.recieveKeepAlive(etherPacket)
+ d.PkgType = l2oam.OnuPkgTypeA
+ } else if (l2oam.OnuPkgType == l2oam.OnuPkgTypeB) && (packet.IsOnuPkgB()) {
+ logger.Info(context.Background(), fmt.Sprintf("[%s] recieveKeepAlive() Pkg type B.", d.getDeviceName()))
+ if d.keepAliveResponse != nil {
+ d.keepAliveResponse <- "success"
+ }
+ d.Base.recieveKeepAlive(etherPacket)
+ d.PkgType = l2oam.OnuPkgTypeB
+ } else {
+ logger.Error(context.Background(), fmt.Sprintf("[%s] recieveKeepAlive() Pkg type error. support type=%s, isOnuPkgA=%v, isOnuPkgB=%v", d.getDeviceName(), l2oam.OnuPkgType, packet.IsOnuPkgA(), packet.IsOnuPkgB()))
+ if d.keepAliveResponse != nil {
+ d.keepAliveResponse <- "PkgTypeError"
+ }
+ }
+}
+
+func (d *L2oamOnuDevice) startKeepAlive() {
+ d.Base.startKeepAlive()
+}
+func (d *L2oamOnuDevice) stopKeepAlive() {
+ d.Base.stopKeepAlive()
+}
+func (d *L2oamOnuDevice) setActiveState(ctx context.Context, isActive bool) {
+ d.Base.setActiveState(ctx, isActive)
+}
+func (d *L2oamOnuDevice) update(mac string, deviceID string, onuID uint32) {
+ mapMutex.Lock()
+ defer mapMutex.Unlock()
+ d.OnuID = onuID
+ d.Base.DeviceID = deviceID
+ deviceMap[mac] = d
+ deviceMacMap[deviceID] = mac
+}
+
+func (d *L2oamOnuDevice) startMountSequence(ctx context.Context, pkgType string, cmd *L2oamCmd) {
+ if pkgType == l2oam.OnuPkgTypeA {
+ d.startMountSequencePkgA(ctx, pkgType, cmd)
+ return
+ }
+ go func() {
+ logger.Debug(ctx, fmt.Sprintf("[%s] startMountSequence(). MacAddress=%s", d.getDeviceName(), d.Base.DstMac))
+ // wait until the onu keep-alive state transits to step3
+ for {
+ if d.Base.KeepAliveStatus != KeepAliveStep3 {
+ time.Sleep(1 * time.Second)
+ } else {
+ logger.Debug(ctx, fmt.Sprintf("[%s] startMountSequence(). onu active status. start sequence. MacAddress=%s", d.getDeviceName(), d.Base.DstMac))
+ break
+ }
+ }
+
+ // ONU System Info(get request)
+ _, err := d.sendAndWait(l2oam.GenerateOnuSystemInfo(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateOnuSystemInfo() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // ONU Config
+ if pkgType == l2oam.OnuPkgTypeB {
+ _, err = d.sendAndWait(l2oam.GenerateOnuConfig(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateOnuConfig() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ }
+ // FEC Mode
+ _, err = d.sendAndWait(l2oam.GenerateFecMode(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateFecMode() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Encryption Mode
+ max := 2
+ if pkgType == l2oam.OnuPkgTypeB {
+ max = 1
+ }
+ for index := 0; index < max; index++ {
+ _, err = d.sendAndWait(l2oam.GenerateEncryptionMode(pkgType, index+1), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateEncryptionMode(%v) Send Error: %v", d.getDeviceName(), index+1, err))
+ return
+ }
+ }
+
+ // Dyn Learning Mode
+ max = 3
+ if pkgType == l2oam.OnuPkgTypeB {
+ max = 1
+ }
+ for index := 0; index < max; index++ {
+ _, err = d.sendAndWait(l2oam.GenerateDynLearningMode(pkgType, index+1), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateDynLearningMode(%v) Send Error: %v", d.getDeviceName(), index+1, err))
+ return
+ }
+ }
+ // Ponp Temperature
+ _, err = d.sendAndWait(l2oam.GeneratePonpTemperature(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneratePonpTemperature() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Ponp Optical TRx Supply Voltage
+ _, err = d.sendAndWait(l2oam.GeneratePonpOptTRxSupplyVoltage(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneratePonpOptTRxSupplyVoltage() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Ponp Optical Tx Bias Current
+ _, err = d.sendAndWait(l2oam.GeneratePonpOptTxBiasCurrent(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneratePonpOptTxBiasCurrent() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Ponp Optical Tx Output Power
+ _, err = d.sendAndWait(l2oam.GeneratePonpOptTxOutputPower(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneratePonpOptTxOutputPower() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Ponp Optical Rx Output Power
+ _, err = d.sendAndWait(l2oam.GeneratePonpOptRxInputPower(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneratePonpOptRxInputPower() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+
+ // Onu Priority Queue Count
+ max = 2
+ if pkgType == l2oam.OnuPkgTypeB {
+ max = 1
+ }
+ for index := 0; index < max; index++ {
+ _, err = d.sendAndWait(l2oam.GenerateOnuPriorityQueueCount(pkgType, index+1), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateOnuPriorityQueueCount(%v) Send Error: %v", d.getDeviceName(), index+1, err))
+ return
+ }
+ }
+ // Unip Target Queue Priority
+ if pkgType == l2oam.OnuPkgTypeB {
+ _, err = d.sendAndWait(l2oam.GenerateUnipTargetQueuePriority(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateUnipTargetQueuePriority() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ }
+ // Vlan Pon VID Value
+ max = 2
+ if pkgType == l2oam.OnuPkgTypeB {
+ max = 1
+ }
+ for index := 0; index < max; index++ {
+ _, err = d.sendAndWait(l2oam.GenerateSetVlanPonVIDValue(pkgType, index+1, cmd.Itpid, cmd.Ivid), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateSetVlanPonVIDValue(%v) Send Error: %v", d.getDeviceName(), index+1, err))
+ return
+ }
+ }
+ // Vlan Tag Filter
+ max = 0
+ if pkgType == l2oam.OnuPkgTypeB {
+ max = 2
+ }
+ for index := 0; index < max; index++ {
+ _, err = d.sendAndWait(l2oam.GenerateSetVlanTagFilter(pkgType, index+1, cmd.Itpid), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateSetVlanTagFilter(%v) Send Error: %v", d.getDeviceName(), index+1, err))
+ return
+ }
+ }
+ // Frame Processing
+ /*if pkgType == ONU_PKG_TYPE_A {
+ _, err = d.sendAndWait(l2oam.GeneateSetFrameProcessing(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneateOnuSysInfo() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ }*/
+ // Qos Policer Rate
+ max = 1
+ if pkgType == l2oam.OnuPkgTypeB {
+ max = 1
+ }
+ for index := 0; index < max; index++ {
+ _, err = d.sendAndWait(l2oam.GenerateSetQosPolicerRate(pkgType, index+1), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateSetQosPolicerRate(%v) Send Error: %v", d.getDeviceName(), index+1, err))
+ return
+ }
+ }
+ // Qos Policer Burst
+ if pkgType == l2oam.OnuPkgTypeB {
+ _, err = d.sendAndWait(l2oam.GenerateSetQosPolicerBurst(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateSetQosPolicerBurst() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ }
+ // Unip Info
+ _, err = d.sendAndWait(l2oam.GenerateGetUnipInfo(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateGetUnipInfo() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Unip Link Mode
+ _, err = d.sendAndWait(l2oam.GenerateSetUnipLinkMode(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateSetUnipLinkMode() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Unip TOSCOS Conversion
+ _, err = d.sendAndWait(l2oam.GenerateUnipTOSCOSConversion(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateUnipTOSCOSConversion() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // ONU Errored Frame Window
+ _, err = d.sendAndWait(l2oam.GenerateOnuErroredFrameWindow(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateOnuErroredFrameWindow() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // ONU Errord Frame Threshold
+ _, err = d.sendAndWait(l2oam.GenerateOnuErroredFrameWindow(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateOnuErroredFrameWindow() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] startMountSequence(). Normal End.", d.getDeviceName()))
+ }()
+}
+func (d *L2oamOnuDevice) startMountSequencePkgA(ctx context.Context, pkgType string, cmd *L2oamCmd) {
+ go func() {
+ logger.Debug(ctx, fmt.Sprintf("[%s] startMountSequence(). MacAddress=%s", d.getDeviceName(), d.Base.DstMac))
+ // wait until the onu keep-alive state transits to step3
+ for {
+ if d.Base.KeepAliveStatus != KeepAliveStep3 {
+ time.Sleep(1 * time.Second)
+ } else {
+ logger.Debug(ctx, fmt.Sprintf("[%s] startMountSequence(). onu active status. start sequence. MacAddress=%s", d.getDeviceName(), d.Base.DstMac))
+ break
+ }
+ }
+
+ // // Encryption Mode
+ // _, err := d.sendAndWait(l2oam.GenerateEncryptionMode(pkgType, 1), ResponseTimer)
+ // if err != nil {
+ // logger.Error(ctx, fmt.Sprintf("[%s] GenerateEncryptionMode(%v) Send Error: %v", d.getDeviceName(), 1, err))
+ // return
+ // }
+
+ // ONU System Info(get request)
+ _, err := d.sendAndWait(l2oam.GenerateOnuSystemInfo(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateOnuSystemInfo() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // ONU Config
+ if pkgType == l2oam.OnuPkgTypeB {
+ _, err = d.sendAndWait(l2oam.GenerateOnuConfig(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateOnuConfig() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ }
+ // FEC Mode
+ _, err = d.sendAndWait(l2oam.GenerateFecMode(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateFecMode() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Encryption Mode
+ _, err = d.sendAndWait(l2oam.GenerateEncryptionMode(pkgType, 2), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateEncryptionMode(%v) Send Error: %v", d.getDeviceName(), 2, err))
+ return
+ }
+
+ // Dyn Learning Mode
+ max := 3
+ if pkgType == l2oam.OnuPkgTypeB {
+ max = 1
+ }
+ for index := 0; index < max; index++ {
+ _, err = d.sendAndWait(l2oam.GenerateDynLearningMode(pkgType, index+1), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateDynLearningMode(%v) Send Error: %v", d.getDeviceName(), index+1, err))
+ return
+ }
+ }
+ // Ponp Temperature
+ _, err = d.sendAndWait(l2oam.GeneratePonpTemperature(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneratePonpTemperature() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Ponp Optical TRx Supply Voltage
+ _, err = d.sendAndWait(l2oam.GeneratePonpOptTRxSupplyVoltage(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneratePonpOptTRxSupplyVoltage() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Ponp Optical Tx Bias Current
+ _, err = d.sendAndWait(l2oam.GeneratePonpOptTxBiasCurrent(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneratePonpOptTxBiasCurrent() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Ponp Optical Tx Output Power
+ _, err = d.sendAndWait(l2oam.GeneratePonpOptTxOutputPower(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneratePonpOptTxOutputPower() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Ponp Optical Rx Output Power
+ _, err = d.sendAndWait(l2oam.GeneratePonpOptRxInputPower(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneratePonpOptRxInputPower() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+
+ // Onu Priority Queue Count
+ max = 2
+ if pkgType == l2oam.OnuPkgTypeB {
+ max = 1
+ }
+ for index := 0; index < max; index++ {
+ _, err = d.sendAndWait(l2oam.GenerateOnuPriorityQueueCount(pkgType, index+1), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateOnuPriorityQueueCount(%v) Send Error: %v", d.getDeviceName(), index+1, err))
+ return
+ }
+ }
+ // Unip Target Queue Priority
+ if pkgType == l2oam.OnuPkgTypeB {
+ _, err = d.sendAndWait(l2oam.GenerateUnipTargetQueuePriority(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateUnipTargetQueuePriority() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ }
+ // Vlan Pon VID Value
+ max = 2
+ if pkgType == l2oam.OnuPkgTypeB {
+ max = 1
+ }
+ for index := 0; index < max; index++ {
+ _, err = d.sendAndWait(l2oam.GenerateSetVlanPonVIDValue(pkgType, index+1, cmd.Itpid, cmd.Ivid), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateSetVlanPonVIDValue(%v) Send Error: %v", d.getDeviceName(), index+1, err))
+ return
+ }
+ }
+ // Vlan Tag Filter
+ max = 0
+ if pkgType == l2oam.OnuPkgTypeB {
+ max = 2
+ }
+ for index := 0; index < max; index++ {
+ _, err = d.sendAndWait(l2oam.GenerateSetVlanTagFilter(pkgType, index+1, cmd.Itpid), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateSetVlanTagFilter(%v) Send Error: %v", d.getDeviceName(), index+1, err))
+ return
+ }
+ }
+ // Frame Processing
+ /*if pkgType == ONU_PKG_TYPE_A {
+ _, err = d.sendAndWait(l2oam.GeneateSetFrameProcessing(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GeneateOnuSysInfo() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ }*/
+ // Qos Policer Rate
+ max = 1
+ if pkgType == l2oam.OnuPkgTypeB {
+ max = 1
+ }
+ for index := 0; index < max; index++ {
+ _, err = d.sendAndWait(l2oam.GenerateSetQosPolicerRate(pkgType, index+1), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateSetQosPolicerRate(%v) Send Error: %v", d.getDeviceName(), index+1, err))
+ return
+ }
+ }
+ // Qos Policer Burst
+ if pkgType == l2oam.OnuPkgTypeB {
+ _, err = d.sendAndWait(l2oam.GenerateSetQosPolicerBurst(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateSetQosPolicerBurst() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ }
+ // Unip Info
+ _, err = d.sendAndWait(l2oam.GenerateGetUnipInfo(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateGetUnipInfo() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ // Traffic Enable
+ _, err = d.sendAndWait(l2oam.GenerateSetTrafficEnable(pkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] GenerateSetTrafficEnable() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] startMountSequence(). Normal End.", d.getDeviceName()))
+ }()
+}
+
+func (d *L2oamOnuDevice) reboot(ctx context.Context) {
+ _, err := d.sendAndWait(l2oam.GenerateSetResetOnu(l2oam.OnuPkgType), ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] reboot() Send Error: %v", d.getDeviceName(), err))
+ return
+ }
+}
+
+func (d *L2oamOnuDevice) updateMap() {
+ mapMutex.Lock()
+ defer mapMutex.Unlock()
+
+ _, ok := deviceMap[d.Base.DstMac]
+ if ok {
+ deviceMap[d.Base.DstMac] = d
+ }
+}
+func (d *L2oamOnuDevice) setL2oamCmd(cmd *L2oamCmd) {
+}
+func (d *L2oamOnuDevice) getL2oamCmd() *L2oamCmd {
+ return nil
+}
+func (d *L2oamOnuDevice) setObjectContext(oc *l2oam.TomiObjectContext) {
+ d.ObjectContext = oc
+}
+func (d *L2oamOnuDevice) getObjectContext() *l2oam.TomiObjectContext {
+ return d.ObjectContext
+}
+func (d *L2oamOnuDevice) setReferenceTable(tbl *l2oam.GetTrafficControlReferenceTableRes) {
+ d.ReferenceTable = tbl
+}
+func (d *L2oamOnuDevice) getReferenceTable() *l2oam.GetTrafficControlReferenceTableRes {
+ return d.ReferenceTable
+}
+func (d *L2oamOnuDevice) setProfile(down *l2oam.SetGenericActionCreateRes, up *l2oam.SetGenericActionCreateRes) {
+ d.DownProfile = down
+ d.UpProfile = up
+}
+func (d *L2oamOnuDevice) getProfile() (*l2oam.SetGenericActionCreateRes, *l2oam.SetGenericActionCreateRes) {
+ return d.DownProfile, d.UpProfile
+}
+func (d *L2oamOnuDevice) delete(ctx context.Context) {
+ d.Base.delete(ctx)
+}
+func (d *L2oamOnuDevice) setDeleteFlag() {
+ d.Base.setDeleteFlag()
+}
+func (d *L2oamOnuDevice) getDeleteFlag() bool {
+ return d.Base.getDeleteFlag()
+}
+func (d *L2oamOnuDevice) setAutonomousFlag(flag bool) {
+ d.Base.setAutonomousFlag(flag)
+ d.updateMap()
+}
+func (d *L2oamOnuDevice) getAutonomousFlag() bool {
+ return d.Base.getAutonomousFlag()
+}
+func (d *L2oamOnuDevice) receiveEapol(etherPacket *layers.Ethernet) {
+ d.Base.receiveEapol(etherPacket)
+}
+func (d *L2oamOnuDevice) waitKeepALiveResponse() error {
+ select {
+ case result := <-d.keepAliveResponse:
+ if result == "success" {
+ return nil
+ }
+ return fmt.Errorf("waitKeepALiveResponse() error. reason=%s", result)
+
+ case <-time.After(ResponseTimer * time.Second):
+ return fmt.Errorf("waitKeepALiveResponse() error. reason=%s", "timeout")
+ }
+}
+
+func (d *L2oamOnuDevice) addFlow(ctx context.Context, cmd *L2oamCmd) error {
+ return errors.New("onu.addFlow: not implemented")
+}
+
+func (d *L2oamOnuDevice) updateFlow(ctx context.Context, cmd *L2oamCmd) error {
+ return errors.New("onu.updateFlow: not implemented")
+}
+
+func (d *L2oamOnuDevice) removeFlow(ctx context.Context) error {
+ return errors.New("onu.removeFlow: not implemented")
+}
+
+// macAddress must not have colons
+/*
+func isOnuEnable(ctx context.Context, macAddress string) (bool, string) {
+ mac := convertMacString(macAddress)
+ onulist, err := l2oam.ReadOnuStatusList()
+ if err != nil {
+ return false, ""
+ }
+ for _, v := range onulist {
+ if v.MacAddress == mac {
+ if v.AdminState == "ENABLED" {
+ return true, v.ID
+ }
+ }
+ }
+
+ return false, ""
+}
+*/
+
+func updateOltActiveStatus(ctx context.Context, dh *DeviceHandler, isActive bool) {
+ logger.Debug(ctx, fmt.Sprintf("updateOltActiveStatus() isActive=%v", isActive))
+ if dh != nil && dh.coreProxy != nil {
+ var err error
+ if isActive {
+ err = dh.coreProxy.DeviceStateUpdate(ctx, dh.device.Id, voltha.ConnectStatus_REACHABLE, voltha.OperStatus_ACTIVE)
+ } else {
+ err = dh.coreProxy.DeviceStateUpdate(ctx, dh.device.Id, voltha.ConnectStatus_UNREACHABLE, voltha.OperStatus_ACTIVATING)
+ }
+ if err != nil {
+ logger.Debug(ctx, fmt.Sprintf("updateOltActiveStatus() error=%v", err))
+ }
+ } else {
+ logger.Debug(ctx, fmt.Sprintf("updateOltActiveStatus() deviceId=%s, isActive=%v", dh.device.Id, isActive))
+ }
+}
+
+// macAddress must not have colons
+/*
+func updateOnuActiveStatus(ctx context.Context, macAddress string, isActive bool, dh *DeviceHandler) {
+ mac := convertMacString(macAddress)
+ onulist, err := l2oam.ReadOnuStatusList()
+ logger.Debug(ctx, fmt.Sprintf("updateOnuActiveStatus() ReadOnuList() onulist=%v, err=%s, macAddress=%s, mac=%s, isActive=%v", onulist, err, macAddress, mac, isActive))
+ if err != nil {
+ return
+ }
+ for i, v := range onulist {
+ if v.MacAddress == mac {
+ if isActive {
+ onulist[i].OpeState = "ACTIVE"
+ onulist[i].ConnectState = "REACHABLE"
+ dh.coreProxy.DeviceStateUpdate(ctx, v.ID, voltha.ConnectStatus_REACHABLE, voltha.OperStatus_ACTIVE)
+ } else {
+ onulist[i].OpeState = "FAILED"
+ onulist[i].ConnectState = "UNREACHABLE"
+ dh.coreProxy.DeviceStateUpdate(ctx, v.ID, voltha.ConnectStatus_UNREACHABLE, voltha.OperStatus_FAILED)
+ }
+ logger.Debug(ctx, fmt.Sprintf("updateOnuActiveStatus() WriteOnuList() macaddress=%s list=%v", mac, onulist))
+ //l2oam.WriteOnuStatusList(onulist)
+ break
+ }
+ }
+}
+*/
+
+func updateOnuActiveStatus2(ctx context.Context, dh *DeviceHandler, isActive bool, deviceID string) {
+ logger.Debug(ctx, fmt.Sprintf("updateOnuActiveStatus2() deviceId=%s, isActive=%v", deviceID, isActive))
+ if dh != nil && dh.coreProxy != nil {
+ var err error
+ if isActive {
+ err = dh.coreProxy.DeviceStateUpdate(ctx, deviceID, voltha.ConnectStatus_REACHABLE, voltha.OperStatus_ACTIVE)
+ } else {
+ err = dh.coreProxy.DeviceStateUpdate(ctx, deviceID, voltha.ConnectStatus_UNREACHABLE, voltha.OperStatus_ACTIVATING)
+ }
+ if err != nil {
+ logger.Debug(ctx, fmt.Sprintf("updateOnuActiveStatus2() error=%v", err))
+ }
+ } else {
+ logger.Debug(ctx, fmt.Sprintf("updateOnuActiveStatus2() deviceId=%s, isActive=%v", deviceID, isActive))
+ }
+}
+func convertMacString(mac string) string {
+ macbytes := []byte(mac)
+ colonbytes := []byte(":")
+ colon := colonbytes[0]
+
+ resbytes := []byte{}
+ resbytes = append(resbytes, macbytes[0])
+ resbytes = append(resbytes, macbytes[1])
+ resbytes = append(resbytes, colon)
+ resbytes = append(resbytes, macbytes[2])
+ resbytes = append(resbytes, macbytes[3])
+ resbytes = append(resbytes, colon)
+ resbytes = append(resbytes, macbytes[4])
+ resbytes = append(resbytes, macbytes[5])
+ resbytes = append(resbytes, colon)
+ resbytes = append(resbytes, macbytes[6])
+ resbytes = append(resbytes, macbytes[7])
+ resbytes = append(resbytes, colon)
+ resbytes = append(resbytes, macbytes[8])
+ resbytes = append(resbytes, macbytes[9])
+ resbytes = append(resbytes, colon)
+ resbytes = append(resbytes, macbytes[10])
+ resbytes = append(resbytes, macbytes[11])
+ return string(resbytes)
+
+}
+func monitoringOnuStatus() {
+ go WatchStatus(context.Background(), nil)
+}
+func byte2Int(v []byte) uint32 {
+ switch len(v) {
+ case 1:
+ return uint32(v[0])
+ case 2:
+ return uint32(binary.BigEndian.Uint16(v[:]))
+ case 4:
+ return binary.BigEndian.Uint32(v[:])
+ default:
+ return 0
+ }
+}
diff --git a/internal/pkg/core/l2oam_handle.go b/internal/pkg/core/l2oam_handle.go
new file mode 100644
index 0000000..eb8ebb3
--- /dev/null
+++ b/internal/pkg/core/l2oam_handle.go
@@ -0,0 +1,204 @@
+/*
+ * 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
+
+import (
+ "context"
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "time"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/google/gopacket/pcap"
+ oop "github.com/opencord/voltha-protos/v3/go/openolt"
+)
+
+const vlanEnable = true
+const vlanIdManagement = 4090
+
+var svlanTagAuth = []byte{0x88, 0xa8, 0x0f, 0xfb}
+
+// IfName is a Interface Name of this device
+var IfName string
+
+// SrcMac is a MAC address of this device
+var SrcMac string
+
+var l2oamHandle *L2oamHandle
+
+// GetL2oamHandle returns my handle
+func GetL2oamHandle() *L2oamHandle {
+ return l2oamHandle
+}
+
+// L2oamHandle contains handle information
+type L2oamHandle struct {
+ Handle *pcap.Handle
+ ReqCh chan []byte
+}
+
+// NewL2oamHandle L2oamHandle created
+func NewL2oamHandle(ctx context.Context, ifname string, srcMac string) {
+ logger.Debug(ctx, fmt.Sprintf("L2oamHandle start. ifname=%s, srcMac=%s", ifname, srcMac))
+ IfName = ifname
+ SrcMac = srcMac
+ if GetL2oamHandle() == nil {
+ handle, _ := pcap.OpenLive(IfName, int32(1500), false, time.Millisecond*1000)
+ l2oamHandle = &L2oamHandle{
+ Handle: handle,
+ ReqCh: make(chan []byte),
+ }
+ if handle == nil {
+ logger.Debug(ctx, "could not get pcap handle.")
+ } else {
+ go l2oamHandle.startThread(context.Background())
+ }
+ }
+}
+
+// because of each handle object that is provided for a device receive messages respectively,
+// call this method using goroutine
+func (h *L2oamHandle) startThread(ctx context.Context) {
+ logger.Debug(ctx, "L2oamHandle thread start. ")
+
+ var filter string = "ether proto 0xa8c8 or 0x888e or 0x8100 or 0x88a8"
+ err := h.Handle.SetBPFFilter(filter)
+ if err != nil {
+ logger.Error(ctx, "Error: Handle.SetBPFFilter")
+ }
+
+ packetSource := gopacket.NewPacketSource(h.Handle, h.Handle.LinkType())
+
+ for {
+ select {
+ // send a message directly using send method, not sending it via channels
+ case message := <-h.ReqCh:
+ logger.Debug(ctx, fmt.Sprintf("gopacket send. %x", message))
+ if err := h.Handle.WritePacketData(message); err != nil {
+ logger.Debug(ctx, "write-packet-error")
+ }
+ // Packets are received
+ case packet := <-packetSource.Packets():
+ etherLayer := packet.Layer(layers.LayerTypeEthernet)
+ logger.Debug(ctx, fmt.Sprintf("gopacket receive. %x", etherLayer))
+ if etherLayer != nil {
+ // type assertion
+ etherPacket, _ := etherLayer.(*layers.Ethernet)
+ packetBytes := append(etherPacket.Contents, etherPacket.Payload...)
+ logger.Debug(ctx, fmt.Sprintf("gopacket ether receive. %x", packetBytes))
+ if vlanEnable {
+ h.dispatchVlan(ctx, etherPacket)
+ } else {
+ h.dispatch(ctx, etherPacket)
+ }
+ }
+ }
+ }
+}
+func (h *L2oamHandle) dispatch(ctx context.Context, etherPacket *layers.Ethernet) {
+ logger.Debug(ctx, fmt.Sprintf("dispatch(). %x", etherPacket))
+
+ etherType := uint16(etherPacket.EthernetType)
+ if etherType == uint16(layers.EthernetTypeEAPOL) {
+ logger.Debug(ctx, fmt.Sprintf("receive message = EAPOL, Contents=%x, Payload=%x", etherPacket.Contents, etherPacket.Payload))
+ device := FindL2oamDevice(hex.EncodeToString(etherPacket.SrcMAC))
+ if device == nil {
+ // unregisterd device
+ logger.Error(ctx, fmt.Sprintf("Received from an unregistered device. macAddr=%s", etherPacket.SrcMAC))
+ h.packetIn(etherPacket)
+ } else {
+ device.receiveEapol(etherPacket)
+ }
+
+ } else if etherType == 0xa8c8 {
+ bytes := etherPacket.Payload
+ opcode := bytes[0]
+ oampduCode := bytes[3]
+ logger.Debug(ctx, fmt.Sprintf("receive message = 1904.2 OAM opcode=%v oampduCode=%v, %x", opcode, oampduCode, bytes))
+
+ device := FindL2oamDevice(hex.EncodeToString(etherPacket.SrcMAC))
+ if device == nil {
+ // unregisterd device
+ logger.Error(ctx, fmt.Sprintf("Received from an unregistered device. macAddr=%s", etherPacket.SrcMAC))
+ return
+ }
+
+ // OAM: keep-alive message
+ if opcode == 0x03 && oampduCode == 0x00 {
+ device.recieveKeepAlive(etherPacket)
+
+ } else {
+ device.recieve(etherPacket)
+ }
+ } else if etherType == uint16(layers.EthernetTypeDot1Q) {
+ device := FindL2oamDevice(hex.EncodeToString(etherPacket.SrcMAC))
+ if device == nil {
+ // unregisterd device
+ logger.Error(ctx, fmt.Sprintf("Received from an unregistered device. macAddr=%s", etherPacket.SrcMAC))
+ h.packetIn(etherPacket)
+ } else {
+ device.receiveEapol(etherPacket)
+ }
+
+ } else {
+ logger.Error(ctx, fmt.Sprintf("unknown etherType = 0x%X", etherType))
+ }
+}
+func (h *L2oamHandle) dispatchVlan(ctx context.Context, etherPacket *layers.Ethernet) {
+ logger.Debug(ctx, fmt.Sprintf("dispatchVlan(). %x", etherPacket))
+
+ etherType := uint16(etherPacket.EthernetType)
+ if etherType == uint16(layers.EthernetTypeQinQ) {
+ // Remove the VLAN tag
+ vlanPacket := &layers.Dot1Q{}
+ if err := vlanPacket.DecodeFromBytes(etherPacket.Payload, nil); err != nil {
+ logger.Debug(ctx, fmt.Sprintf("error:%v", err))
+ }
+ etherBytes := etherPacket.Contents
+ binary.BigEndian.PutUint16(etherBytes[12:], uint16(vlanPacket.Type))
+ packetBytes := append(etherBytes, vlanPacket.Payload...)
+ etherPacket = &layers.Ethernet{}
+ if err := etherPacket.DecodeFromBytes(packetBytes, nil); err != nil {
+ logger.Debug(ctx, fmt.Sprintf("error:%v", err))
+ }
+ h.dispatch(ctx, etherPacket)
+ } else {
+ logger.Error(ctx, fmt.Sprintf("Discard unsupported etherType. 0x%X", etherType))
+ }
+}
+
+func (h *L2oamHandle) send(message []byte) {
+ if h.Handle != nil {
+ h.ReqCh <- message
+ }
+}
+func (h *L2oamHandle) packetIn(etherPacket *layers.Ethernet) {
+ packetBytes := append(etherPacket.Contents, etherPacket.Payload...)
+ pktInd := &oop.PacketIndication{
+ IntfId: 0,
+ GemportId: 1,
+ PortNo: 16,
+ Pkt: packetBytes,
+ IntfType: "pon",
+ }
+ logger.Info(context.Background(), fmt.Sprintf("L2oamHandle.packetIn() pktInd=%x", pktInd))
+
+ if err := l2oamDeviceHandler.handlePacketIndication(context.Background(), pktInd); err != nil {
+ logger.Error(context.Background(), "L2oamHandle.packetIn() error. ")
+ }
+}
diff --git a/internal/pkg/core/l2oam_if.go b/internal/pkg/core/l2oam_if.go
new file mode 100644
index 0000000..87daab5
--- /dev/null
+++ b/internal/pkg/core/l2oam_if.go
@@ -0,0 +1,1339 @@
+/*
+ * 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
+
+import (
+ "context"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/core/l2oam"
+ oop "github.com/opencord/voltha-protos/v3/go/openolt"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+func readTechProfile() {
+ address := "172.17.0.1:2379"
+ timeout := time.Second * 10
+
+ client, err := kvstore.NewEtcdClient(context.Background(), address, timeout, log.FatalLevel)
+ if err != nil {
+ return
+ }
+ defer client.Close(context.Background())
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ resp, err := client.Get(ctx, "service/voltha/technology_profiles/EPON/64")
+ cancel()
+ if resp == nil || err != nil {
+ l2oam.OnuPkgType = l2oam.OnuPkgTypeB
+ logger.Error(ctx, fmt.Sprintf("readTechProfile() etcd get error. resp=%v, err=%v", resp, err))
+ return
+ }
+ bytes := resp.Value.([]byte)
+ bytesString := string(bytes)
+ logger.Debug(ctx, fmt.Sprintf("readTechProfile() bytes string=%s", bytesString))
+ if strings.Index(bytesString, "\"profile_type\":\"EPON\"") > 0 && strings.Index(bytesString, "\"package_type\":\"B\"") > 0 {
+ l2oam.OnuPkgType = l2oam.OnuPkgTypeB
+ } else {
+ l2oam.OnuPkgType = l2oam.OnuPkgTypeA
+ }
+ logger.Debug(ctx, fmt.Sprintf("readTechProfile() onu package=%s", l2oam.OnuPkgType))
+
+}
+
+// L2oamGetDeviceInfo provides GetDeviceInfo for L2OAM
+func L2oamGetDeviceInfo(ctx context.Context, dh *DeviceHandler) (*oop.DeviceInfo, error) {
+ logger.Debug(ctx, "GetDeviceInfo() Start.")
+ readTechProfile()
+
+ dstMac := dh.device.MacAddress
+ deviceInfo := &oop.DeviceInfo{
+ Vendor: "Tibit",
+ // Model string `protobuf:"bytes,2,opt,name=model,proto3" json:"model,omitempty"`
+ // HardwareVersion string `protobuf:"bytes,3,opt,name=hardware_version,json=hardwareVersion,proto3" json:"hardware_version,omitempty"`
+ // FirmwareVersion string `protobuf:"bytes,4,opt,name=firmware_version,json=firmwareVersion,proto3" json:"firmware_version,omitempty"`
+ DeviceId: dstMac,
+ // DeviceSerialNumber string `protobuf:"bytes,17,opt,name=device_serial_number,json=deviceSerialNumber,proto3" json:"device_serial_number,omitempty"`
+ PonPorts: 1,
+ Technology: "EPON",
+ OnuIdStart: 1,
+ OnuIdEnd: 128,
+ AllocIdStart: 5121,
+ AllocIdEnd: 9216,
+ GemportIdStart: 1,
+ GemportIdEnd: 65535,
+ FlowIdStart: 1,
+ FlowIdEnd: 16383,
+ // Ranges []*DeviceInfo_DeviceResourceRanges protobuf:"bytes,15,rep,name=ranges,proto3" json:"ranges,omitempty"`
+ }
+ olt := FindL2oamDevice(dstMac)
+ if olt == nil {
+ olt, _ = NewL2oamOltDevice(dstMac, dh)
+ olt.startKeepAlive()
+ }
+
+ err00 := sendDiscoverySOLICIT(ctx, olt)
+ logger.Debug(ctx, fmt.Sprintf("Sequence returts: %v", err00))
+ vendorName, err01 := sendGetVendorName(ctx, olt)
+ moduleNumber, err02 := sendGetModuleNumber(ctx, olt)
+ manuFacturer, err03 := sendManuFacturerInfo(ctx, olt)
+ firmwareVersion, err04 := sendGetFirmwareVersion(ctx, olt)
+ macAddress, err05 := sendGetMacAddress(ctx, olt)
+ serialNumber, err06 := sendGetSerialNumber(ctx, olt)
+ deviceInfo.Vendor = vendorName
+ deviceInfo.Model = moduleNumber
+ deviceInfo.HardwareVersion = manuFacturer
+ deviceInfo.FirmwareVersion = firmwareVersion
+ deviceInfo.DeviceId = macAddress
+ deviceInfo.DeviceSerialNumber = serialNumber
+ logger.Debug(ctx, fmt.Sprintf("Sequence returts: deviceInfo=%v, %v, %v, %v, %v, %v, %v, %v", deviceInfo, err00, err01, err02, err03, err04, err05, err06))
+
+ dh.device.SerialNumber = deviceInfo.DeviceSerialNumber
+ olt.setActiveState(ctx, true)
+ return deviceInfo, nil
+}
+
+// L2oamEnableIndication runs EnableIndication sequence
+func L2oamEnableIndication(ctx context.Context, dh *DeviceHandler) {
+ logger.Debug(ctx, "L2oamEnableIndication() Start.")
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamEnableIndication() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ return
+ }
+
+ err01 := sendSetHbtxPeriod(ctx, olt)
+ err02 := sendSetHbtxTemplate(ctx, olt)
+ ponMode, err03 := sendGetPonMode(ctx, olt)
+ err04 := sendSetMPCPSync(ctx, olt)
+ err05 := sendSetAdminState(ctx, olt, true)
+ ponPortAction := sendSetGenericActionCreate(ctx, olt, l2oam.ActionTypeProtocolFilter)
+ if ponPortAction == nil {
+ return
+ }
+ actionIDPonPort := binary.BigEndian.Uint32(ponPortAction.GetTrafficProfile())
+ err07 := sendSetIngressPort(ctx, olt, actionIDPonPort, true)
+ err08 := sendSetCaptureProtocols(ctx, olt, actionIDPonPort)
+ ethPortAction := sendSetGenericActionCreate(ctx, olt, l2oam.ActionTypeProtocolFilter)
+ if ethPortAction == nil {
+ return
+ }
+ actionIDEthPort := binary.BigEndian.Uint32(ethPortAction.GetTrafficProfile())
+ err09 := sendSetIngressPort(ctx, olt, actionIDEthPort, false)
+ err10 := sendSetCaptureProtocols(ctx, olt, actionIDEthPort)
+
+ oltDev := olt.(*L2oamOltDevice)
+ oltDev.Base.ActionIds = make(map[int]uint32)
+ oltDev.Base.ActionIds[ActionIDFilterPonPort] = actionIDPonPort
+ oltDev.Base.ActionIds[ActionIDFilterEthPort] = actionIDEthPort
+ olt.updateMap()
+
+ err06 := sendSetAdminState(ctx, olt, false)
+ olt.setAutonomousFlag(true)
+
+ logger.Debug(ctx, fmt.Sprintf("Sequence returns: ponMode=%s, %v, %v, %v, %v, %v, %v, %v, %v, %v, %v",
+ ponMode, err01, err02, err03, err04, err05, err06, err07, err08, err09, err10))
+
+}
+
+// L2oamAfterKeepAlive ... [obsoluted]
+func L2oamAfterKeepAlive(ctx context.Context, dh *DeviceHandler) error {
+
+
+
+ return nil
+}
+
+//var flowVersion int = 1 // run add-flow each OLT
+//var flowVersion int = 2 // run add-flow each ONU
+var flowVersion int = 3 // run add-flow at specified ONU
+
+// L2oamAddFlow runs add-flow sequence
+func L2oamAddFlow(ctx context.Context, dh *DeviceHandler, cmd *L2oamCmd) {
+ logger.Debug(ctx, "L2oamAddFlow() Start.")
+ if cmd.OnuDeviceID == "" {
+ flowVersion = 2
+ } else {
+ flowVersion = 3
+ }
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamAddFlow() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ return
+ }
+
+ if flowVersion == 1 {
+ oc := &l2oam.TomiObjectContext{
+ Branch: 0x0c,
+ Type: 0x0011,
+ Length: 4,
+ Instance: 0x00000002,
+ }
+ getTrafficControl := sendGetTrafficControlReferenceTable(ctx, olt, oc)
+ if getTrafficControl == nil {
+ logger.Error(ctx, "L2oamAddFlow() sendGetTrafficControlReferenceTable() error. ")
+ return
+ }
+ downProfile := sendSetGenericActionCreate(ctx, olt, l2oam.ActionTypeTrafficProfile)
+ if downProfile == nil {
+ logger.Error(ctx, "L2oamAddFlow() sendSetGenericActionCreate(down) error. ")
+ return
+ }
+ upProfile := sendSetGenericActionCreate(ctx, olt, l2oam.ActionTypeTrafficProfile)
+ if upProfile == nil {
+ logger.Error(ctx, "L2oamAddFlow() sendSetGenericActionCreate(up) error. ")
+ return
+ }
+ olt.setReferenceTable(getTrafficControl)
+ olt.setProfile(downProfile, upProfile)
+ olt.updateMap()
+
+ err01 := sendSetTrafficControl(ctx, olt, getTrafficControl.GetReferenceControlDown(), downProfile.GetTrafficProfile())
+ err02 := sendSetTrafficControl(ctx, olt, getTrafficControl.GetReferenceControlUp(), upProfile.GetTrafficProfile())
+
+ err03 := sendSetPriority(ctx, olt, downProfile.GetTrafficProfile())
+ err04 := sendSetGuranteedRate(ctx, olt, cmd.Cir, downProfile.GetTrafficProfile())
+ err05 := sendSetGuranteedRate(ctx, olt, cmd.Cir, upProfile.GetTrafficProfile())
+ err06 := sendSetBestEffortRate(ctx, olt, cmd.Pir, downProfile.GetTrafficProfile())
+ err07 := sendSetBestEffortRate(ctx, olt, cmd.Pir, upProfile.GetTrafficProfile())
+
+ logger.Debug(ctx, fmt.Sprintf("Sequence results: %v, %v, %v, %v, %v, %v, %v", err01, err02, err03, err04, err05, err06, err07))
+ } else if flowVersion == 2 {
+ // add-flow to all ONUs
+ // Traffic Profile(priority, guranteed rate, best effort rate) are set as common settings
+ onuDevices, err := dh.coreProxy.GetChildDevices(ctx, dh.device.Id)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("catnnot-get-child-devices: %v", err))
+ return
+ }
+
+ isFirst := true
+ var downProfile *l2oam.SetGenericActionCreateRes
+ var upProfile *l2oam.SetGenericActionCreateRes
+ for _, onuDevice := range onuDevices.Items {
+ //onu := FindL2oamDevice(onuDevice.MacAddress)
+ onu := FindL2oamDeviceByDeviceID(onuDevice.Id)
+ if onu == nil {
+ logger.Error(ctx, fmt.Sprintf("catnnot-get-onu-device. device-id:%s, mac-address:%s",
+ onuDevice.Id, onuDevice.MacAddress))
+ continue
+ }
+ if !(onu.(*L2oamOnuDevice)).Base.isActive() {
+ logger.Debug(ctx, fmt.Sprintf("onu is not active. device-id:%s, mac-address:%s",
+ onuDevice.Id, onuDevice.MacAddress))
+ continue
+ }
+ logger.Debug(ctx, fmt.Sprintf("add-flow-for-onu. device-id:%s, mac-address:%s",
+ onuDevice.Id, onuDevice.MacAddress))
+
+ oc := onu.getObjectContext()
+ if oc == nil {
+ logger.Error(ctx, fmt.Sprintf("catnnot-get-onu-object-context. device-id:%s, mac-address:%s",
+ onuDevice.Id, onuDevice.MacAddress))
+ continue
+ }
+ getTrafficControl := sendGetTrafficControlReferenceTable(ctx, olt, oc)
+ if getTrafficControl == nil {
+ logger.Error(ctx, "L2oamAddFlow() sendGetTrafficControlReferenceTable() error. ")
+ continue
+ }
+ onu.setReferenceTable(getTrafficControl)
+ onu.updateMap()
+
+ // create one Traffic Profile at OLT
+ if isFirst {
+ downProfile = sendSetGenericActionCreate(ctx, olt, l2oam.ActionTypeTrafficProfile)
+ if downProfile == nil {
+ logger.Error(ctx, "L2oamAddFlow() sendSetGenericActionCreate(down) error. ")
+ break
+ }
+ upProfile = sendSetGenericActionCreate(ctx, olt, l2oam.ActionTypeTrafficProfile)
+ if upProfile == nil {
+ logger.Error(ctx, "L2oamAddFlow() sendSetGenericActionCreate(up) error. ")
+ break
+ }
+ olt.setProfile(downProfile, upProfile)
+ olt.updateMap()
+ }
+
+ err01 := sendSetTrafficControl(ctx, olt, getTrafficControl.GetReferenceControlDown(), downProfile.GetTrafficProfile())
+ err02 := sendSetTrafficControl(ctx, olt, getTrafficControl.GetReferenceControlUp(), upProfile.GetTrafficProfile())
+ logger.Debug(ctx, fmt.Sprintf("traffic-control/traffic-profile results: %v, %v", err01, err02))
+
+ if isFirst {
+ err03 := sendSetPriority(ctx, olt, downProfile.GetTrafficProfile())
+ err04 := sendSetGuranteedRate(ctx, olt, cmd.Cir, downProfile.GetTrafficProfile())
+ err05 := sendSetGuranteedRate(ctx, olt, cmd.Cir, upProfile.GetTrafficProfile())
+ err06 := sendSetBestEffortRate(ctx, olt, cmd.Pir, downProfile.GetTrafficProfile())
+ err07 := sendSetBestEffortRate(ctx, olt, cmd.Pir, upProfile.GetTrafficProfile())
+ isFirst = false
+ logger.Debug(ctx, fmt.Sprintf("traffic-profile-settings-results: %v, %v, %v, %v, %v",
+ err03, err04, err05, err06, err07))
+ }
+ }
+ } else if flowVersion == 3 {
+ if err := olt.addFlow(ctx, cmd); err != nil {
+ logger.Errorf(ctx, "failed-to-add-flow: %v", err)
+ }
+ }
+}
+
+// L2oamAddFlowAndMount adds flow to device and start mount sequence
+func (d *DeviceHandler) L2oamAddFlowAndMount(ctx context.Context, onuDeviceID string, vid []byte, ivid []byte) {
+ l2oamCmd := &L2oamCmd{
+ Type: "add-flow-dev",
+ Tpid: []byte{0x88, 0xa8},
+ Vid: vid,
+ Itpid: []byte{0x81, 0x00},
+ Ivid: ivid,
+ OnuDeviceID: onuDeviceID,
+ }
+ olt := FindL2oamDeviceByDeviceID(d.device.Id)
+ if olt == nil {
+ logger.Errorf(ctx, "olt not found.")
+ }
+ if err := olt.updateFlow(ctx, l2oamCmd); err != nil {
+ logger.Errorf(ctx, "failed-to-update-flow. %v", err)
+ } else {
+ onu := FindL2oamDeviceByDeviceID(onuDeviceID)
+ if onu == nil {
+ logger.Debug(ctx, fmt.Sprintf("L2oamAddFlowAndMount() FindL2oamDevice() onu not found. deviceId=%s", onuDeviceID))
+ } else {
+ // start ONU mount sequence
+ onu.startMountSequence(ctx, l2oam.OnuPkgType, l2oamCmd)
+ }
+ }
+}
+
+// L2oamAddFlowToDeviceAll adds a flow to specified device.
+// If some flows are added already, all flows are removed from all devices before adding.
+func L2oamAddFlowToDeviceAll(ctx context.Context, dh *DeviceHandler, cmd *L2oamCmd) error {
+ logger.Debug(ctx, "L2oamAddFlowToDeviceAll() Start.")
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamAddFlowToDeviceAll() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ return nil
+ }
+
+ oc := olt.getObjectContext()
+ var onuList []*L2oamOnuDevice
+ onuDeviceID := cmd.OnuDeviceID
+ onu := FindL2oamDeviceByDeviceID(onuDeviceID)
+ if onu == nil {
+ return nil
+ }
+ onuDev, ok := onu.(*L2oamOnuDevice)
+ if !ok {
+ return nil
+ }
+ if oc != nil {
+ onuDevices, err := dh.coreProxy.GetChildDevices(ctx, dh.device.Id)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("catnnot-get-child-devices: %v", err))
+ return err
+ }
+ // create flow added onu list
+ reflow := false
+ for _, onuDevice := range onuDevices.Items {
+ //onu := FindL2oamDevice(onuDevice.MacAddress)
+ onu := FindL2oamDeviceByDeviceID(onuDevice.Id)
+ if onu != nil {
+ if onuDev, ok := onu.(*L2oamOnuDevice); ok {
+ if onuDev.Base.FlowAdded {
+ onuList = append(onuList, onuDev)
+ if onuDeviceID == onuDev.Base.DeviceID {
+ reflow = true
+ }
+ }
+ }
+ }
+ }
+ if !reflow {
+ onuList = append(onuList, onuDev)
+ }
+ L2oamRemoveFlowFromDevice(ctx, dh, onuDevices)
+ } else {
+ onuList = append(onuList, onuDev)
+ }
+ localCmd := *cmd
+ for _, onuDev := range onuList {
+ localCmd.OnuDeviceID = onuDev.Base.DeviceID
+ if err := L2oamAddFlowToDevice(ctx, dh, &localCmd); err != nil {
+ continue
+ }
+ }
+
+ return nil
+}
+
+// L2oamAddFlowToDevice runs add-flow-device sequence
+func L2oamAddFlowToDevice(ctx context.Context, dh *DeviceHandler, cmd *L2oamCmd) error {
+ if flowVersion == 1 {
+ oc := L2oamAddFlowToDeviceDS(ctx, dh, cmd)
+ if oc == nil {
+ return errors.New("failed-to-add-flow-to-device-for-down-stream")
+ }
+ L2oamAddFlowToDeviceUS(ctx, dh, oc, cmd)
+ } else if flowVersion > 1 {
+ logger.Debug(ctx, "L2oamAddFlowToDevice() Start.")
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamAddFlowToDevice() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ return nil
+ }
+
+ // add flow for downstream
+ oc := olt.getObjectContext()
+ var err01 error
+ var err02 error
+ onuDeviceID := cmd.OnuDeviceID
+ // "Generic/Action Create, L2 Switching Domain/Action Inlet entry" is created only first
+ if oc == nil {
+ oc, err01 = sendSetGenericActionCreateForDS(ctx, olt)
+ if oc == nil {
+ logger.Error(ctx, fmt.Sprintf("error. %v", err01))
+ return errors.New("failed-to-add-flow-to-device-for-down-stream")
+ }
+ // TODO ignore tpid, vid after the second time
+ err02 = sendSetL2SwitchingDomainForDS(ctx, olt, oc, cmd.Tpid, cmd.Vid)
+ olt.setL2oamCmd(cmd)
+ olt.setObjectContext(oc)
+ if oltDev, ok := olt.(*L2oamOltDevice); ok {
+ oltDev.Base.FlowAdded = true
+ }
+ olt.updateMap()
+ } else {
+ cmd = olt.getL2oamCmd()
+ }
+
+ onu := FindL2oamDeviceByDeviceID(onuDeviceID)
+ if onu == nil {
+ logger.Debug(ctx, fmt.Sprintf("L2oamCmdRequest() FindL2oamDevice() onu not found. deviceId=%s", onuDeviceID))
+ } else {
+ onuID := onu.getObjectContext()
+ if onuID == nil {
+ logger.Error(ctx, "catnnot-get-onu-object-context.")
+ } else {
+ err03 := sendSetDefaultOutlet(ctx, olt, oc, onuID)
+ err04 := sendSetL2SwitchingDomainForUS(ctx, olt, oc, cmd.Tpid, cmd.Vid, onuID)
+
+ logger.Debug(ctx, fmt.Sprintf("Sequence results: %v, %v, %v, %v", err01, err02, err03, err04))
+ if onuDev, ok := onu.(*L2oamOnuDevice); ok {
+ onuDev.Base.FlowAdded = true
+ onu.updateMap()
+ } else {
+ logger.Error(ctx, fmt.Sprintf("assertion failed. device-id:%s", onuDeviceID))
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// L2oamAddFlowToDeviceDS runs add-flow-device sequence for downstream
+func L2oamAddFlowToDeviceDS(ctx context.Context, dh *DeviceHandler, cmd *L2oamCmd) *l2oam.TomiObjectContext {
+ logger.Debug(ctx, "L2oamAddFlowToDeviceDS() Start.")
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamAddFlowToDeviceDS() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ }
+
+ oc, err01 := sendSetGenericActionCreateForDS(ctx, olt)
+ if oc != nil {
+ onuID := &l2oam.TomiObjectContext{
+ Branch: 0x0c,
+ Type: 0x0011,
+ Length: 4,
+ Instance: 0x00000001,
+ }
+ err02 := sendSetL2SwitchingDomainForDS(ctx, olt, oc, cmd.Tpid, cmd.Vid)
+ err03 := sendSetDefaultOutlet(ctx, olt, oc, onuID)
+ logger.Debug(ctx, fmt.Sprintf("Sequence results: %v, %v, %v", err01, err02, err03))
+ } else {
+ logger.Debug(ctx, fmt.Sprintf("Sequence results: %v", err01))
+ }
+ olt.setL2oamCmd(cmd)
+ olt.setObjectContext(oc)
+ olt.updateMap()
+
+ return oc
+}
+
+// L2oamAddFlowToDeviceUS runs add-flow-device sequence for upstream
+func L2oamAddFlowToDeviceUS(ctx context.Context, dh *DeviceHandler, oc *l2oam.TomiObjectContext, cmd *L2oamCmd) {
+ logger.Debug(ctx, "L2oamAddFlowToDeviceUS() Start.")
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamAddFlowToDeviceUS() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ }
+
+ onuID := &l2oam.TomiObjectContext{
+ Branch: 0x0c,
+ Type: 0x0011,
+ Length: 4,
+ Instance: 0x00000002,
+ }
+ err01 := sendSetL2SwitchingDomainForUS(ctx, olt, oc, cmd.Tpid, cmd.Vid, onuID)
+
+ logger.Debug(ctx, fmt.Sprintf("Sequence results: %v", err01))
+}
+
+// L2oamRebootDevice reboots the device
+func L2oamRebootDevice(ctx context.Context, dh *DeviceHandler, device *voltha.Device) {
+ logger.Debug(ctx, "L2oamRebootDevice() Start.")
+
+ L2oamDisableOlt(ctx, dh)
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamRebootDevice() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ return
+ }
+
+ err01 := sendSetActionReset(ctx, olt)
+
+ logger.Debug(ctx, fmt.Sprintf("Sequence results: %v", err01))
+
+}
+
+// L2oamDeleteOlt deletes the OLT device
+func L2oamDeleteOlt(ctx context.Context, dh *DeviceHandler) {
+ logger.Debug(ctx, fmt.Sprintf("L2oamDeleteOlt() Start. deviceId=%s", dh.device.Id))
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamDeleteOlt() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ return
+ }
+ olt.setAutonomousFlag(false)
+ if !SetupL2oamDeleteFlag(dh.device.Id) {
+ logger.Warn(ctx, fmt.Sprintf("L2oamDeleteOlt() olt deleted deviceId=%s", dh.device.Id))
+ return
+ }
+ err01 := sendSetAdminState(ctx, olt, true)
+ DeleteAllDevice()
+
+ logger.Debug(ctx, fmt.Sprintf("L2oamDeleteOlt() End. deviceId=%s, %v", dh.device.Id, err01))
+}
+
+// L2oamDisableOlt disables the OLT device
+func L2oamDisableOlt(ctx context.Context, dh *DeviceHandler) {
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamDisableOlt() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ return
+ }
+ olt.setAutonomousFlag(false)
+
+ onuDevices, err := dh.coreProxy.GetChildDevices(ctx, dh.device.Id)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("catnnot-get-child-devices: %v", err))
+ }
+
+ if err = olt.removeFlow(ctx); err != nil {
+ logger.Error(ctx, fmt.Sprintf("failed-to-remove-flows: %v", err))
+ }
+ L2oamRemoveFlow(ctx, dh, onuDevices)
+ L2oamChildDeviceLost(ctx, dh, onuDevices)
+ L2oamDisableDevice(ctx, dh)
+ resetFlowMap()
+}
+
+// L2oamRemoveFlowFromDevice removes flows from the device
+func L2oamRemoveFlowFromDevice(ctx context.Context, dh *DeviceHandler, onuDevices *voltha.Devices) {
+ logger.Debug(ctx, "L2oamRemoveFlowFromDeviceDS() Start.")
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamRemoveFlowFromDeviceDS() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ }
+ oc := olt.getObjectContext()
+ cmd := olt.getL2oamCmd()
+ if cmd == nil {
+ logger.Warn(ctx, fmt.Sprintf("L2oamRemoveFlowFromDeviceDS() tpid, vid are not specified. deviceId=%s",
+ dh.device.Id))
+ return
+ }
+ logger.Debug(ctx, fmt.Sprintf("TPID:%v, VID:%v", cmd.Tpid, cmd.Vid))
+
+ for _, onuDevice := range onuDevices.Items {
+ //onu := FindL2oamDevice(onuDevice.MacAddress)
+ onu := FindL2oamDeviceByDeviceID(onuDevice.Id)
+ if onu == nil {
+ logger.Error(ctx, fmt.Sprintf("catnnot-get-onu-device. device-id:%s, mac-address:%s",
+ onuDevice.Id, onuDevice.MacAddress))
+ continue
+ }
+ var onuDev *L2oamOnuDevice
+ var ok bool
+ if onuDev, ok = onu.(*L2oamOnuDevice); ok {
+ if !onuDev.Base.FlowAdded {
+ logger.Error(ctx, fmt.Sprintf("flow is not added. device-id:%s, mac-address:%s",
+ onuDevice.Id, onuDevice.MacAddress))
+ continue
+ }
+ } else {
+ logger.Error(ctx, fmt.Sprintf("assertion failed. device-id:%s, mac-address:%s",
+ onuDevice.Id, onuDevice.MacAddress))
+ continue
+ }
+ onuID := onu.getObjectContext()
+ if onuID == nil {
+ logger.Error(ctx, fmt.Sprintf("catnnot-get-onu-object-context. device-id:%s, mac-address:%s",
+ onuDevice.Id, onuDevice.MacAddress))
+ continue
+ }
+ logger.Debug(ctx, fmt.Sprintf("Switching Domain/Action Remove Inlet entry. device-id:%s, instance-id:%d",
+ onuDevice.Id, onuID.Instance))
+ err := sendSetActionInletEntryUsDel(ctx, olt, oc, cmd.Tpid, cmd.Vid, onuID)
+ logger.Debug(ctx, fmt.Sprintf("Sequence results: %v", err))
+ onuDev.Base.FlowAdded = false
+ onu.updateMap()
+ }
+
+ if oltDev, ok := olt.(*L2oamOltDevice); ok {
+ if oltDev.Base.FlowAdded {
+ err02 := sendSetActionInletEntryDsDel(ctx, olt, oc, cmd.Tpid, cmd.Vid)
+ err03 := sendSetActionDeleteStream(ctx, olt, oc)
+
+ logger.Debug(ctx, fmt.Sprintf("Sequence results: %v, %v", err02, err03))
+ olt.setObjectContext(nil)
+ oltDev.Base.FlowAdded = false
+ olt.updateMap()
+ } else {
+ logger.Debug(ctx, fmt.Sprintf("flow is not added. device-id:%s", dh.device.Id))
+ }
+ } else {
+ logger.Debug(ctx, fmt.Sprintf("assersion failed. device-id:%s", dh.device.Id))
+ }
+}
+
+// L2oamRemoveFlow removes flows
+func L2oamRemoveFlow(ctx context.Context, dh *DeviceHandler, onuDevices *voltha.Devices) {
+ logger.Debug(ctx, "L2oamRemoveFlow() Start.")
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamRemoveFlow() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ }
+
+ if flowVersion == 2 {
+ //refTbl := olt.getReferenceTable()
+ downProfile, upProfile := olt.getProfile()
+ //if refTbl == nil || downProfile == nil || upProfile == nil {
+ if downProfile == nil || upProfile == nil {
+ logger.Warn(ctx, fmt.Sprintf("L2oamRemoveFlow() profiles are not specified. deviceId=%s",
+ dh.device.Id))
+ return
+ }
+ downInstanceID := downProfile.GetTrafficProfile()
+ upInstanceID := upProfile.GetTrafficProfile()
+ //err01 := sendSetTrafficProfile(ctx, olt,
+ // refTbl.GetReferenceControlDown(), downInstanceId)
+ //err02 := sendSetTrafficProfile(ctx, olt,
+ // refTbl.GetReferenceControlUp(), upInstanceId)
+ err03 := sendSetActionDelete(ctx, olt, downInstanceID, l2oam.ActionTypeTrafficProfile)
+ err04 := sendSetActionDelete(ctx, olt, upInstanceID, l2oam.ActionTypeTrafficProfile)
+ olt.setProfile(nil, nil)
+ olt.updateMap()
+
+ //logger.Debug(ctx, fmt.Sprintf("Sequence results: %v, %v, %v, %v", err01, err02, err03, err04))
+ logger.Debug(ctx, fmt.Sprintf("Sequence results: %v, %v", err03, err04))
+ } else if flowVersion == 3 {
+ for _, onuDevice := range onuDevices.Items {
+ logger.Debugf(ctx, "remove-traffic-schedule. onu-device-id:%s", onuDevice.Id)
+ onu := FindL2oamDeviceByDeviceID(onuDevice.Id)
+ if onu == nil {
+ logger.Errorf(ctx, "catnnot-get-onu-device")
+ continue
+ }
+ refTbl := onu.getReferenceTable()
+ if refTbl == nil {
+ logger.Debug(ctx, "traffic-profile-is-not-created")
+ continue
+ }
+ downProfile, upProfile := onu.getProfile()
+ if downProfile == nil || upProfile == nil {
+ logger.Debug(ctx, "flow is not added")
+ continue
+ }
+ downInstanceID := downProfile.GetTrafficProfile()
+ upInstanceID := upProfile.GetTrafficProfile()
+ err01 := sendSetTrafficProfile(ctx, olt,
+ refTbl.GetReferenceControlDown(), downInstanceID)
+ err02 := sendSetTrafficProfile(ctx, olt,
+ refTbl.GetReferenceControlUp(), upInstanceID)
+ err03 := sendSetActionDelete(ctx, olt, downInstanceID, l2oam.ActionTypeTrafficProfile)
+ err04 := sendSetActionDelete(ctx, olt, upInstanceID, l2oam.ActionTypeTrafficProfile)
+ onu.setReferenceTable(nil)
+ onu.setProfile(nil, nil)
+ onu.updateMap()
+ logger.Debug(ctx, fmt.Sprintf("remove-traffic-schedule-results: %v, %v, %v, %v", err01, err02, err03, err04))
+ }
+ }
+}
+
+// L2oamChildDeviceLost sends DeviceLost message
+func L2oamChildDeviceLost(ctx context.Context, dh *DeviceHandler, onuDevices *voltha.Devices) {
+ logger.Debug(ctx, "L2oamChildDeviceLost() Start.")
+ if onuDevices == nil {
+ return
+ }
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamChildDeviceLost() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ }
+
+ for _, onuDevice := range onuDevices.Items {
+ //onu := FindL2oamDevice(onuDevice.MacAddress)
+ onu := FindL2oamDeviceByDeviceID(onuDevice.Id)
+ if onu == nil {
+ logger.Error(ctx, fmt.Sprintf("catnnot-get-onu-device. device-id:%s, mac-address:%s",
+ onuDevice.Id, onuDevice.MacAddress))
+ } else {
+ oc := onu.getObjectContext()
+ if oc == nil {
+ logger.Error(ctx, fmt.Sprintf("catnnot-get-onu-object-context. device-id:%s, mac-address:%s",
+ onuDevice.Id, onuDevice.MacAddress))
+ } else {
+ logger.Debug(ctx, fmt.Sprintf("onu-object-context. instance-id:%x", oc.Instance))
+ err01 := sendSetActionDeleteOnu(ctx, olt, oc)
+
+ logger.Debug(ctx, fmt.Sprintf("Sequence results: %v", err01))
+ }
+ }
+ }
+}
+
+// L2oamDisableDevice disables the device
+func L2oamDisableDevice(ctx context.Context, dh *DeviceHandler) {
+ logger.Debug(ctx, "L2oamDisableDevice() Start.")
+
+ olt := FindL2oamDeviceByDeviceID(dh.device.Id)
+ if olt == nil {
+ logger.Error(ctx, fmt.Sprintf("L2oamDisableDevice() FindL2oamDeviceByDeviceId() error. olt not found. deviceId=%s", dh.device.Id))
+ }
+
+ oltDev := olt.(*L2oamOltDevice)
+ if oltDev.Base.ActionIds != nil {
+ var bytes [4]byte
+ if id, ok := oltDev.Base.ActionIds[ActionIDFilterPonPort]; ok {
+ binary.BigEndian.PutUint32(bytes[:], id)
+ if err := sendSetActionDelete(ctx, olt, bytes[:], l2oam.ActionTypeProtocolFilter); err != nil {
+ return
+ }
+ }
+ if id, ok := oltDev.Base.ActionIds[ActionIDFilterEthPort]; ok {
+ binary.BigEndian.PutUint32(bytes[:], id)
+ if err := sendSetActionDelete(ctx, olt, bytes[:], l2oam.ActionTypeProtocolFilter); err != nil {
+ return
+ }
+ }
+ oltDev.Base.ActionIds = nil
+ olt.updateMap()
+ }
+ err01 := sendSetAdminState(ctx, olt, true)
+ err02 := sendSetManagementLock(ctx, olt)
+
+ logger.Debug(ctx, fmt.Sprintf("Sequence results: %v, %v", err01, err02))
+}
+
+func sendDiscoverySOLICIT(ctx context.Context, device L2oamDevice) error {
+ if err := device.send(l2oam.GenerateDiscoverySOLICIT()); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendDiscoverySOLICIT() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.DiscoveryHello{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendDiscoverySOLICIT() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return nil
+}
+
+func sendGetVendorName(ctx context.Context, device L2oamDevice) (string, error) {
+ if err := device.send(l2oam.GnerateGetVendorName()); err != nil {
+ return "", nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return "", fmt.Errorf("[%s] sendGetVendorName() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.GetVendorNameRes{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return "", nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendGetVendorName() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return packet.GetVendorName(), nil
+}
+
+func sendGetModuleNumber(ctx context.Context, device L2oamDevice) (string, error) {
+ if err := device.send(l2oam.GenerateGetModuleNumber()); err != nil {
+ return "", nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return "", fmt.Errorf("[%s] sendGetModuleNumber() Send Error: %v", device.getDeviceName(), err)
+ }
+ packet := &l2oam.GetModuleNumberRes{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return "", nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendGetModuleNumber() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return packet.GetModuleNumber(), nil
+}
+
+func sendManuFacturerInfo(ctx context.Context, device L2oamDevice) (string, error) {
+ if err := device.send(l2oam.GenerateManuFacturerInfo()); err != nil {
+ return "", nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return "", fmt.Errorf("[%s] sendManuFacturerInfo() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.GetManufacturerRes{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return "", nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendManuFacturerInfo() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return packet.GetManufacturer(), nil
+}
+
+func sendGetFirmwareVersion(ctx context.Context, device L2oamDevice) (string, error) {
+ if err := device.send(l2oam.GenerateGetFirmwareVersion()); err != nil {
+ return "", nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return "", fmt.Errorf("[%s] sendGetFirmwareVersion() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.GetFirmwareVersionRes{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return "", nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendGetFirmwareVersion() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return packet.GetFirmwareVersionNumber(), nil
+}
+
+func sendGetMacAddress(ctx context.Context, device L2oamDevice) (string, error) {
+ if err := device.send(l2oam.GenerateGetMacAddress()); err != nil {
+ return "", nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return "", fmt.Errorf("[%s] sendGetMacAddress() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.GetMacAddressRes{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return "", nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendGetMacAddress() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return packet.GetMacAddress(), nil
+}
+
+func sendGetSerialNumber(ctx context.Context, device L2oamDevice) (string, error) {
+ if err := device.send(l2oam.GenerateGetSerialNumber()); err != nil {
+ return "", nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return "", fmt.Errorf("[%s] sendGetSerialNumber() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.GetSerialNumberRes{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return "", nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendGetSerialNumber() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return packet.GetSerialNumber(), nil
+}
+
+func sendSetHbtxTemplate(ctx context.Context, device L2oamDevice) error {
+ if err := device.send(l2oam.GenerateSetHbtxTemplate()); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetHbtxTemplate() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.TOAMSetResponse{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetHbtxTemplate() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return nil
+}
+
+func sendGetPonMode(ctx context.Context, device L2oamDevice) (string, error) {
+ if err := device.send(l2oam.GenerateGetPonMode()); err != nil {
+ return "", nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return "", fmt.Errorf("[%s] sendGetPonMode() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.GetPonMode{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return "", nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendGetPonMode() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return packet.GetPonMode(), nil
+}
+
+func sendSetMPCPSync(ctx context.Context, device L2oamDevice) error {
+ if err := device.send(l2oam.GenerateSetMPCPSync()); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetMPCPSync() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.TOAMSetResponse{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetMPCPSync() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return nil
+}
+
+func sendSetAdminState(ctx context.Context, device L2oamDevice, isDelete bool) error {
+ if isDelete {
+ if err := device.send(l2oam.GenerateSetAdminStateDelete()); err != nil {
+ return nil
+ }
+ } else {
+ if err := device.send(l2oam.GenerateSetAdminState()); err != nil {
+ return nil
+ }
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetAdminState() Send Error: isDelete=%v, %v", device.getDeviceName(), isDelete, err)
+ }
+
+ packet := &l2oam.TOAMSetResponse{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetAdminState() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return nil
+}
+
+func sendSetHbtxPeriod(ctx context.Context, device L2oamDevice) error {
+ if err := device.send(l2oam.GenerateSetHbtxPeriod()); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetHbtxPeriod() Send Error: %v", device.getDeviceName(), err)
+ }
+ packet := &l2oam.TOAMSetResponse{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetHbtxPeriod() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+
+ return nil
+}
+
+func sendSetIngressPort(ctx context.Context, device L2oamDevice, actionID uint32, isPonPort bool) error {
+ if err := device.send(l2oam.GenerateIngressPort(actionID, isPonPort)); err != nil {
+ return nil
+ }
+ _, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("sendSetIngressPort() send error. %s ", err))
+ return nil
+ }
+
+ return nil
+}
+
+func sendSetCaptureProtocols(ctx context.Context, device L2oamDevice, actionID uint32) error {
+ if err := device.send(l2oam.GenerateCaptureProtocols(actionID)); err != nil {
+ return nil
+ }
+ _, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("sendSetCaptureProtocols() send error. %s ", err))
+ return nil
+ }
+
+ return nil
+}
+
+func sendGetTrafficControlReferenceTable(ctx context.Context, device L2oamDevice, oc *l2oam.TomiObjectContext) *l2oam.GetTrafficControlReferenceTableRes {
+ if err := device.send(l2oam.GenerateGetTrafficControlReferenceTableReq(oc)); err != nil {
+ return nil
+ }
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("sendGetTrafficControlReferenceTable() send error. %s ", err))
+ return nil
+ }
+ packet := &l2oam.GetTrafficControlReferenceTableRes{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return nil
+ }
+ return packet
+}
+
+func sendSetGenericActionCreate(ctx context.Context, device L2oamDevice, actionType int) *l2oam.SetGenericActionCreateRes {
+ if err := device.send(l2oam.GenerateGenericActionCreate(actionType)); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] sendSetGenericActionCreate() Send Error: %v", device.getDeviceName(), err))
+ return nil
+ }
+ packet := &l2oam.SetGenericActionCreateRes{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetGenericActionCreate() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+
+ return packet
+}
+
+func sendSetTrafficControl(ctx context.Context, device L2oamDevice, trafficControl []byte, trafficProfile []byte) error {
+
+ if err := device.send(l2oam.GenerateTrafficControlTrafficProfile(trafficControl, trafficProfile)); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetTrafficControl() Send Error: %v", device.getDeviceName(), err)
+ }
+ packet := &l2oam.TOAMSetResponse{}
+
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetTrafficControl() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+
+ return nil
+}
+
+func sendSetPriority(ctx context.Context, device L2oamDevice, trafficProfile []byte) error {
+ if err := device.send(l2oam.GeneratePriority(trafficProfile)); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetPriority() Send Error: %v", device.getDeviceName(), err)
+ }
+ packet := &l2oam.TOAMSetResponse{}
+
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetPriority() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+
+ return nil
+}
+
+func sendSetGuranteedRate(ctx context.Context, device L2oamDevice, cir []byte, trafficProfile []byte) error {
+ if err := device.send(l2oam.GenerateGuaranteedRate(cir, trafficProfile)); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetGuranteedRate() Send Error: %v", device.getDeviceName(), err)
+ }
+ packet := &l2oam.TOAMSetResponse{}
+
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetGuranteedRate() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+
+ return nil
+}
+
+// func sendSetGuranteedMbs(ctx context.Context, device L2oamDevice) error {
+// if err := device.send(l2oam.GenerateGuaranteedMbs()); err != nil {
+// return nil
+// }
+
+// ether, err := device.waitResponse(ResponseTimer)
+// if err != nil {
+// return fmt.Errorf("[%s] sendSetGuranteedMbs() Send Error: %v", device.getDeviceName(), err)
+// }
+// packet := &l2oam.TOAMSetResponse{}
+// //packet.Decode(ether.Payload)
+
+// logger.Debug(ctx, fmt.Sprintf("[%s] sendSetGuranteedMbs() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+
+// return nil
+// }
+
+func sendSetBestEffortRate(ctx context.Context, device L2oamDevice, pir []byte, trafficProfile []byte) error {
+ if err := device.send(l2oam.GenerateBestEffortRate(pir, trafficProfile)); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetBestEffortRate() Send Error: %v", device.getDeviceName(), err)
+ }
+ packet := &l2oam.TOAMSetResponse{}
+
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetBestEffortRate() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+
+ return nil
+}
+
+// func sendSetBestEffortMbs(ctx context.Context, device L2oamDevice) error {
+// if err := device.send(l2oam.GenerateBestEffortMbs()); err != nil {
+// return nil
+// }
+
+// ether, err := device.waitResponse(ResponseTimer)
+// if err != nil {
+// return fmt.Errorf("[%s] sendSetBestEffortMbs() Send Error: %v", device.getDeviceName(), err)
+// }
+// packet := &l2oam.TOAMSetResponse{}
+// //packet.Decode(ether.Payload)
+
+// logger.Debug(ctx, fmt.Sprintf("[%s] sendSetBestEffortMbs() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+
+// return nil
+// }
+
+func sendSetGenericActionCreateForDS(ctx context.Context, device L2oamDevice) (*l2oam.TomiObjectContext, error) {
+ if err := device.send(l2oam.GenerateGenericActionCreateDs()); err != nil {
+ return nil, nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return nil, fmt.Errorf("[%s] sendSetGenericActionCreateForDS() Send Error: %v", device.getDeviceName(), err)
+ }
+ oc := &l2oam.TomiObjectContext{}
+
+ i := 29
+ oc.Branch = ether.Payload[i]
+ i++
+ oc.Type = binary.BigEndian.Uint16(ether.Payload[i : i+2])
+ i += 2
+ oc.Length = ether.Payload[i]
+ i++
+ oc.Instance = binary.BigEndian.Uint32(ether.Payload[i : i+4])
+
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetGenericActionCreateForDS() Success, response=%x, branch=%x, type=%x, length=%x, instance=%x",
+ device.getDeviceName(), ether.Payload, oc.Branch, oc.Type, oc.Length, oc.Instance))
+
+ return oc, nil
+}
+
+func sendSetL2SwitchingDomainForDS(ctx context.Context, device L2oamDevice, oc *l2oam.TomiObjectContext, tpid []byte, vid []byte) error {
+ tibitData := l2oam.GenerateL2switchingDomainActionLnletEntryDs(oc, tpid, vid)
+ if err := device.send(tibitData); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetL2SwitchingDomain() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.TOAMSetResponse{}
+
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetL2SwitchingDomain() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+
+ return nil
+}
+
+func sendSetDefaultOutlet(ctx context.Context, device L2oamDevice, oc *l2oam.TomiObjectContext, onuID *l2oam.TomiObjectContext) error {
+ tibitData := l2oam.GenerateSetDefaultOutlet(oc, onuID)
+ if err := device.send(tibitData); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetDefaultOutlet() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.TOAMSetResponse{}
+
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetDefaultOutlet() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+
+ return nil
+}
+
+func sendSetL2SwitchingDomainForUS(ctx context.Context, device L2oamDevice, oc *l2oam.TomiObjectContext,
+ tpid []byte, vid []byte, onuID *l2oam.TomiObjectContext) error {
+ tibitData := l2oam.GenerateL2switchingDomainActionLnletEntryUs(oc, tpid, vid, onuID)
+ if err := device.send(tibitData); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetL2SwitchingDomainForUS() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.TOAMSetResponse{}
+
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetL2SwitchingDomainForUS() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+
+ return nil
+}
+
+func sendSetActionReset(ctx context.Context, device L2oamDevice) error {
+ if err := device.send(l2oam.GenerateSetActionReset()); err != nil {
+ return nil
+ }
+
+ ether, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetActionReset() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ packet := &l2oam.TOAMSetResponse{}
+ if err := packet.Decode(ether.Payload); err != nil {
+ return nil
+ }
+ logger.Debug(ctx, fmt.Sprintf("[%s] sendSetActionReset() Success, response=%x:%s", device.getDeviceName(), ether.Payload, packet))
+ return nil
+}
+
+func sendSetTrafficProfile(ctx context.Context, device L2oamDevice,
+ trafficControl []byte, trafficProfile []byte) error {
+ if err := device.send(l2oam.GenerateSetTrafficProfile(trafficControl, trafficProfile)); err != nil {
+ return err
+ }
+
+ if _, err := device.waitResponse(ResponseTimer); err != nil {
+ return fmt.Errorf("[%s] sendSetTrafficProfile() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ return nil
+}
+
+func sendSetActionDelete(ctx context.Context, device L2oamDevice, trafficProfile []byte, actionType int) error {
+ if err := device.send(l2oam.GenerateSetActionDelete(trafficProfile, actionType)); err != nil {
+ return nil
+ }
+
+ _, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetActionDelete() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ return nil
+}
+
+func sendSetActionDeleteOnu(ctx context.Context, device L2oamDevice, oc *l2oam.TomiObjectContext) error {
+ logger.Debug(ctx, "sendSetActionDeleteOnu() Start.")
+ if err := device.send(l2oam.GenerateSetActionDeleteOnu(oc)); err != nil {
+ return nil
+ }
+
+ _, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetActionDeleteOnu() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ return nil
+}
+
+func sendSetManagementLock(ctx context.Context, device L2oamDevice) error {
+ if err := device.send(l2oam.GenerateSetManagementLock()); err != nil {
+ return nil
+ }
+
+ _, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetManagementLock() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ return nil
+}
+
+func sendSetActionInletEntryDsDel(ctx context.Context, device L2oamDevice,
+ oc *l2oam.TomiObjectContext, tpid []byte, vid []byte) error {
+ if err := device.send(l2oam.GenerateL2switchingDomainActionInletEntryDsDel(oc, tpid, vid)); err != nil {
+ return nil
+ }
+
+ _, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetActionInletEntryDsDel() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ return nil
+}
+
+func sendSetActionInletEntryUsDel(ctx context.Context, device L2oamDevice,
+ oc *l2oam.TomiObjectContext, tpid []byte, vid []byte, onuID *l2oam.TomiObjectContext) error {
+ if err := device.send(l2oam.GenerateL2switchingDomainActionInletEntryUsDel(oc, tpid, vid, onuID)); err != nil {
+ return nil
+ }
+
+ _, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetActionInletEntryUsDel() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ return nil
+}
+
+func sendSetActionDeleteStream(ctx context.Context, device L2oamDevice, oc *l2oam.TomiObjectContext) error {
+ if err := device.send(l2oam.GenerateSetActionDeleteStream(oc)); err != nil {
+ return nil
+ }
+
+ _, err := device.waitResponse(ResponseTimer)
+ if err != nil {
+ return fmt.Errorf("[%s] sendSetActionDeleteStream() Send Error: %v", device.getDeviceName(), err)
+ }
+
+ return nil
+}
diff --git a/internal/pkg/core/l2oam_oltctl.go b/internal/pkg/core/l2oam_oltctl.go
new file mode 100644
index 0000000..10f9f76
--- /dev/null
+++ b/internal/pkg/core/l2oam_oltctl.go
@@ -0,0 +1,108 @@
+/*
+ * 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
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "time"
+)
+
+type AddFlowParam struct {
+ Value1 string `json:"value1"`
+ Value2 string `json:"value2"`
+ Value3 string `json:"value3"`
+}
+type AddFlowDownParam struct {
+ Value1 string `json:"value1"`
+ Value2 string `json:"value2"`
+ Value3 string `json:"value3"`
+}
+type AddFlowUpParam struct {
+ Value1 string `json:"value1"`
+ Value2 string `json:"value2"`
+ Value3 string `json:"value3"`
+}
+
+type OltCtlJson struct {
+ Command string `json:"command"`
+ Command_help []string `json:"command_help"`
+ AddFlowParam AddFlowParam `json:"addFlowParam"`
+ AddFlowDownParam AddFlowDownParam `json:"addFlowDownParam"`
+ AddFlowUpParam AddFlowUpParam `json:"addFlowUpParam"`
+}
+
+func init() {
+}
+
+func oltctl_main() {
+ for {
+ time.Sleep(1 * time.Second)
+ jsonData := readJson()
+ if jsonData == nil {
+ continue
+ }
+
+ switch jsonData.Command {
+ case "AddFlow":
+ oltctl_AddFlow(&jsonData.AddFlowParam)
+ case "AddFlowDown":
+ oltctl_AddFlowDown(&jsonData.AddFlowDownParam)
+ case "AddFlowUp":
+ oltctl_AddFlowUp(&jsonData.AddFlowUpParam)
+ default:
+
+ }
+ jsonData.Command = "None"
+ writeJson(jsonData)
+
+ }
+
+}
+func oltctl_AddFlow(param *AddFlowParam) {
+ logger.Debug(context.Background(), fmt.Sprintf("oltctl_AddFlow() %v", param))
+}
+func oltctl_AddFlowDown(param *AddFlowDownParam) {
+ logger.Debug(context.Background(), fmt.Sprintf("oltctl_AddFlowDown() %v", param))
+}
+func oltctl_AddFlowUp(param *AddFlowUpParam) {
+ logger.Debug(context.Background(), fmt.Sprintf("oltctl_AddFlowUp() %v", param))
+}
+
+func readJson() *OltCtlJson {
+ bytes, err := ioutil.ReadFile(os.Getenv("HOME") + "/oltctl.json")
+ if err != nil {
+ fmt.Printf("json read error. %v\n", err)
+ return nil
+ }
+ jsonData := &OltCtlJson{}
+ if err := json.Unmarshal(bytes, jsonData); err != nil {
+ fmt.Printf("json unmarshal error. %v\n", err)
+ return nil
+ }
+ return jsonData
+}
+func writeJson(jsonData *OltCtlJson) {
+
+ bytes, err := json.Marshal(jsonData)
+ if err != nil {
+ return
+ }
+ ioutil.WriteFile(os.Getenv("HOME")+"/oltctl.json", bytes, 0644)
+}
diff --git a/internal/pkg/core/olt_onu_msg.go b/internal/pkg/core/olt_onu_msg.go
new file mode 100644
index 0000000..10336b7
--- /dev/null
+++ b/internal/pkg/core/olt_onu_msg.go
@@ -0,0 +1,86 @@
+/*
+ * 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
+
+import (
+ "context"
+ "encoding/json"
+
+ "github.com/golang/protobuf/ptypes"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+ oop "github.com/opencord/voltha-protos/v3/go/openolt"
+)
+
+// AdditionalMessage is used for something...
+type AdditionalMessage struct {
+}
+
+func (dh *DeviceHandler) receivedMsgFromOnu(ctx context.Context, msg *ic.InterAdapterMessage) error {
+ msgBody := msg.GetBody()
+ ind := &oop.OnuIndication{}
+
+ err := ptypes.UnmarshalAny(msgBody, ind)
+ if err != nil {
+ logger.Debugw(ctx, "cannot-unmarshal-onu-indication-body", log.Fields{"error": err})
+ return err
+ }
+
+ var info AdditionalMessage
+ err = json.Unmarshal(ind.SerialNumber.VendorSpecific, &info)
+ if err != nil {
+ logger.Debugw(ctx, "cannot-unmarshal-additional-message", log.Fields{"error": err})
+ return err
+ }
+
+ onuDevice := FindL2oamDeviceByDeviceID(msg.Header.ToDeviceId)
+ if onuDevice == nil {
+ logger.Error(ctx, "receivedMsgFromOnu() OnuDevice is not found. deviceId=%s", msg.Header.ToDeviceId)
+ return nil
+ }
+
+ id := msg.Header.Id
+ switch id {
+ case "reboot":
+ onuDevice.reboot(ctx)
+ case "disable":
+ logger.Warn(ctx, "unsupported message, Id=%s", id)
+ case "reenable":
+ logger.Warn(ctx, "unsupported message, Id=%s", id)
+ }
+ return nil
+}
+
+/*
+func (dh *DeviceHandler) sendMsgToOnu(ctx context.Context, deviceID string, topic string, msg string, option interface{}) error {
+ bytes, _ := json.Marshal(option)
+ info := oop.OnuIndication{
+ SerialNumber: &oop.SerialNumber{
+ VendorSpecific: bytes,
+ },
+ }
+
+ err := dh.AdapterProxy.SendInterAdapterMessage(ctx, &info,
+ ic.InterAdapterMessageType_ONU_IND_REQUEST,
+ dh.device.Type, topic,
+ deviceID, dh.device.Id, msg)
+
+ if err != nil {
+ logger.Debugw(ctx, "err-sending-message", log.Fields{"device-id": deviceID, "topic": topic, "error": err})
+ }
+ return err
+}
+*/
diff --git a/internal/pkg/core/olt_platform.go b/internal/pkg/core/olt_platform.go
new file mode 100644
index 0000000..9d4596b
--- /dev/null
+++ b/internal/pkg/core/olt_platform.go
@@ -0,0 +1,269 @@
+/*
+ * 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"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/olterrors"
+ 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 (
+ bitsForUniID = 4
+ bitsForONUID = 8
+ bitsForPONID = 8
+ MaxOnusPerPon = (1 << bitsForONUID)
+ MaxPonsPerOlt = (1 << bitsForPONID)
+ MaxUnisPerOnu = (1 << bitsForUniID)
+ nniUniDiffPos = (bitsForUniID + bitsForONUID + bitsForPONID)
+ ponIntfMarkerPos = 28
+ ponIntfMarkerValue = 0x2
+ bitsforNNIID = 20
+ minNniIntPortNum = (1 << bitsforNNIID)
+ 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(ctx context.Context, intfID, onuID, uniID uint32) uint32 {
+ var limit = int(onuID)
+ if limit > MaxOnusPerPon {
+ logger.Warn(ctx, "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(ctx context.Context, portNum uint32) (uint32, error) {
+ if portNum < minNniIntPortNum || portNum > maxNniPortNum {
+ logger.Errorw(ctx, "nniportnumber-is-not-in-valid-range", log.Fields{"portnum": portNum})
+ return uint32(0), olterrors.ErrInvalidPortRange
+ }
+ return (portNum & 0xFFFF), nil
+}
+
+//IntfIDToPortTypeName returns port type derived from the intfId
+func IntfIDToPortTypeName(intfID uint32) voltha.Port_PortType {
+ if ((ponIntfMarkerValue << ponIntfMarkerPos) ^ intfID) < MaxPonsPerOlt {
+ return voltha.Port_PON_OLT
+ }
+ if (intfID & (1 << nniUniDiffPos)) == (1 << nniUniDiffPos) {
+ return voltha.Port_ETHERNET_NNI
+ }
+ return voltha.Port_ETHERNET_UNI
+}
+
+//ExtractAccessFromFlow returns AccessDevice information
+func ExtractAccessFromFlow(inPort, outPort uint32) (uint32, uint32, uint32, uint32) {
+ if IsUpstream(outPort) {
+ return inPort, IntfIDFromUniPortNum(inPort), OnuIDFromPortNum(inPort), UniIDFromPortNum(inPort)
+ }
+ return outPort, IntfIDFromUniPortNum(outPort), OnuIDFromPortNum(outPort), UniIDFromPortNum(outPort)
+}
+
+//IsUpstream returns true for Upstream and false for downstream
+func IsUpstream(outPort uint32) bool {
+ for _, port := range controllerPorts {
+ if port == outPort {
+ return true
+ }
+ }
+ return (outPort & (1 << nniUniDiffPos)) == (1 << nniUniDiffPos)
+}
+
+//IsControllerBoundFlow returns true/false
+func IsControllerBoundFlow(outPort uint32) bool {
+ for _, port := range controllerPorts {
+ if port == outPort {
+ return true
+ }
+ }
+ return false
+}
+
+//OnuIDFromUniPortNum returns onuId from give portNum information.
+func OnuIDFromUniPortNum(portNum uint32) uint32 {
+ return (portNum >> bitsForUniID) & (MaxOnusPerPon - 1)
+}
+
+//FlowExtractInfo fetches uniport from the flow, based on which it gets and returns ponInf, onuID, uniID, inPort and ethType
+func FlowExtractInfo(ctx context.Context, 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, olterrors.NewErrNotFound("pon-interface", log.Fields{"flow-direction": flowDirection}, nil)
+ }
+
+ ponIntf = IntfIDFromUniPortNum(uniPortNo)
+ onuID = OnuIDFromUniPortNum(uniPortNo)
+ uniID = UniIDFromPortNum(uniPortNo)
+
+ logger.Debugw(ctx, "flow-extract-info-result",
+ log.Fields{
+ "uniportno": uniPortNo,
+ "pon-intf": ponIntf,
+ "onu-id": onuID,
+ "uni-id": 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..c8e661c
--- /dev/null
+++ b/internal/pkg/core/olt_platform_test.go
@@ -0,0 +1,383 @@
+/*
+ * 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 (
+ "context"
+ "math"
+ "reflect"
+ "testing"
+
+ fu "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/olterrors"
+ 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(context.Background(), 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, olterrors.ErrInvalidPortRange},
+ {"IntfIDFromNniPortNum-02", args{portNum: 9090}, 0, olterrors.ErrInvalidPortRange},
+ {"IntfIDFromNniPortNum-03", args{portNum: 0}, 0, olterrors.ErrInvalidPortRange},
+ {"IntfIDFromNniPortNum-04", args{portNum: 65535}, 0, olterrors.ErrInvalidPortRange},
+ {"IntfIDFromNniPortNum-05", args{portNum: 1048575}, 0, olterrors.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, olterrors.ErrInvalidPortRange},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := IntfIDFromNniPortNum(context.Background(), 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
+ }
+ 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)
+ }
+ })
+ }
+}
+
+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)),
+ 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(context.Background(), 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..9a11a3b
--- /dev/null
+++ b/internal/pkg/core/olt_state_transitions.go
@@ -0,0 +1,170 @@
+/*
+ * 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 DeviceState = iota
+ deviceStateInit
+ deviceStateConnected
+ deviceStateUp
+ deviceStateDown
+)
+
+// Trigger for changing the state
+type Trigger int
+
+const (
+ DeviceInit Trigger = iota
+ GrpcConnected
+ DeviceUpInd
+ DeviceDownInd
+ 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)
+ transitionMap.transitions[DeviceInit] =
+ Transition{
+ previousState: []DeviceState{deviceStateNull, deviceStateUp, deviceStateDown},
+ currentState: deviceStateInit,
+ before: []TransitionHandler{dh.doStateInit},
+ after: []TransitionHandler{dh.postInit}}
+ transitionMap.transitions[GrpcDisconnected] =
+ Transition{
+ previousState: []DeviceState{deviceStateConnected, deviceStateDown},
+ currentState: deviceStateInit,
+ before: []TransitionHandler{dh.doStateInit},
+ after: []TransitionHandler{dh.postInit}}
+ transitionMap.transitions[GrpcConnected] =
+ Transition{
+ previousState: []DeviceState{deviceStateInit},
+ currentState: deviceStateConnected,
+ before: []TransitionHandler{dh.doStateConnected}}
+
+ transitionMap.transitions[DeviceUpInd] =
+ Transition{
+ previousState: []DeviceState{deviceStateConnected, deviceStateDown},
+ currentState: deviceStateUp,
+ before: []TransitionHandler{dh.doStateUp}}
+ 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 {
+ 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) {
+
+ if !tMap.isValidTransition(trigger) {
+ logger.Errorw(ctx, "invalid-transition-triggered",
+ log.Fields{
+ "current-state": tMap.currentDeviceState,
+ "trigger": trigger})
+ return
+ }
+
+ beforeHandlers := tMap.transitions[trigger].before
+ if beforeHandlers == nil {
+ logger.Debugw(ctx, "no-handlers-for-before", log.Fields{"trigger": trigger})
+ }
+ for _, handler := range beforeHandlers {
+ logger.Debugw(ctx, "running-before-handler", log.Fields{"handler": funcName(handler)})
+ if err := handler(ctx); err != nil {
+ // TODO handle error
+ logger.Error(ctx, err)
+ return
+ }
+ }
+
+ tMap.currentDeviceState = tMap.transitions[trigger].currentState
+ logger.Debugw(ctx, "updated-device-state ", log.Fields{"current-device-state": tMap.currentDeviceState})
+
+ afterHandlers := tMap.transitions[trigger].after
+ if afterHandlers == nil {
+ logger.Debugw(ctx, "no-handlers-for-after", log.Fields{"trigger": trigger})
+ }
+ for _, handler := range afterHandlers {
+ logger.Debugw(ctx, "running-after-handler", log.Fields{"handler": funcName(handler)})
+ if err := handler(ctx); err != nil {
+ // TODO handle error
+ logger.Error(ctx, 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..9c2be8d
--- /dev/null
+++ b/internal/pkg/core/olt_state_transitions_test.go
@@ -0,0 +1,189 @@
+/*
+ * 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"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/olterrors"
+ "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 olterrors.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 olterrors.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..58b18e5
--- /dev/null
+++ b/internal/pkg/core/openolt.go
@@ -0,0 +1,379 @@
+/*
+ * 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 (
+ "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"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/olterrors"
+ 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
+ KVStoreAddress string
+ 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.KVStoreAddress = cfg.KVStoreAddress
+ 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 {
+ logger.Info(ctx, "starting-device-manager")
+ logger.Info(ctx, "device-manager-started")
+ return nil
+}
+
+//Stop terminates the session
+func (oo *OpenOLT) Stop(ctx context.Context) error {
+ logger.Info(ctx, "stopping-device-manager")
+ oo.exitChannel <- 1
+ logger.Info(ctx, "device-manager-stopped")
+ return nil
+}
+
+func (oo *OpenOLT) addDeviceHandlerToMap(agent *DeviceHandler) {
+ oo.lockDeviceHandlersMap.Lock()
+ defer oo.lockDeviceHandlersMap.Unlock()
+ if _, exist := oo.deviceHandlers[agent.device.Id]; !exist {
+ oo.deviceHandlers[agent.device.Id] = agent
+ }
+}
+
+func (oo *OpenOLT) deleteDeviceHandlerToMap(agent *DeviceHandler) {
+ oo.lockDeviceHandlersMap.Lock()
+ defer oo.lockDeviceHandlersMap.Unlock()
+ delete(oo.deviceHandlers, agent.device.Id)
+}
+
+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
+}
+
+// Adopt_device creates a new device handler if not present already and then adopts the device
+func (oo *OpenOLT) Adopt_device(ctx context.Context, device *voltha.Device) error {
+ if device == nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"device": nil}, nil).Log()
+ }
+ logger.Infow(ctx, "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(ctx context.Context, device *voltha.Device) (*ic.SwitchCapability, error) {
+ logger.Infow(ctx, "Get_ofp_device_info", log.Fields{"deviceId": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ return handler.GetOfpDeviceInfo(device)
+ }
+ return nil, olterrors.NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil)
+}
+
+//Process_inter_adapter_message sends messages to a target device (between adapters)
+func (oo *OpenOLT) Process_inter_adapter_message(ctx context.Context, msg *ic.InterAdapterMessage) error {
+ logger.Debugw(ctx, "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(ctx, msg)
+ }
+ return olterrors.NewErrNotFound("device-handler", log.Fields{"device-id": targetDevice}, nil)
+}
+
+//Adapter_descriptor not implemented
+func (oo *OpenOLT) Adapter_descriptor(ctx context.Context) error {
+ return olterrors.ErrNotImplemented
+}
+
+//Device_types unimplemented
+func (oo *OpenOLT) Device_types(ctx context.Context) (*voltha.DeviceTypes, error) {
+ return nil, olterrors.ErrNotImplemented
+}
+
+//Health returns unimplemented
+func (oo *OpenOLT) Health(ctx context.Context) (*voltha.HealthStatus, error) {
+ return nil, olterrors.ErrNotImplemented
+}
+
+//Reconcile_device unimplemented
+func (oo *OpenOLT) Reconcile_device(ctx context.Context, device *voltha.Device) error {
+ if device == nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"device": nil}, nil)
+ }
+ logger.Infow(ctx, "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(ctx context.Context, device *voltha.Device) error {
+ return olterrors.ErrNotImplemented
+}
+
+//Disable_device disables the given device
+func (oo *OpenOLT) Disable_device(ctx context.Context, device *voltha.Device) error {
+ logger.Infow(ctx, "disable-device", log.Fields{"deviceId": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ return handler.DisableDevice(ctx, device)
+ }
+ return olterrors.NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil)
+}
+
+//Reenable_device enables the olt device after disable
+func (oo *OpenOLT) Reenable_device(ctx context.Context, device *voltha.Device) error {
+ logger.Infow(ctx, "reenable-device", log.Fields{"deviceId": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ return handler.ReenableDevice(ctx, device)
+ }
+ return olterrors.NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil)
+}
+
+//Reboot_device reboots the given device
+func (oo *OpenOLT) Reboot_device(ctx context.Context, device *voltha.Device) error {
+ logger.Infow(ctx, "reboot-device", log.Fields{"deviceId": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ return handler.RebootDevice(ctx, device)
+ }
+ return olterrors.NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil)
+}
+
+//Self_test_device unimplented
+func (oo *OpenOLT) Self_test_device(ctx context.Context, device *voltha.Device) error {
+ return olterrors.ErrNotImplemented
+}
+
+//Delete_device unimplemented
+func (oo *OpenOLT) Delete_device(ctx context.Context, device *voltha.Device) error {
+ logger.Infow(ctx, "delete-device", log.Fields{"deviceId": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ if err := handler.DeleteDevice(ctx, device); err != nil {
+ logger.Errorw(ctx, "failed-to-handle-delete-device", log.Fields{"device-id": device.Id})
+ }
+ oo.deleteDeviceHandlerToMap(handler)
+ return nil
+ }
+ return olterrors.NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil)
+}
+
+//Get_device_details unimplemented
+func (oo *OpenOLT) Get_device_details(ctx context.Context, device *voltha.Device) error {
+ return olterrors.ErrNotImplemented
+}
+
+//Update_flows_bulk returns
+func (oo *OpenOLT) Update_flows_bulk(ctx context.Context, device *voltha.Device, flows *voltha.Flows, groups *voltha.FlowGroups, flowMetadata *voltha.FlowMetadata) error {
+ return olterrors.ErrNotImplemented
+}
+
+//Update_flows_incrementally updates (add/remove) the flows on a given device
+func (oo *OpenOLT) Update_flows_incrementally(ctx context.Context, device *voltha.Device, flows *openflow_13.FlowChanges, groups *openflow_13.FlowGroupChanges, flowMetadata *voltha.FlowMetadata) error {
+ logger.Debugw(ctx, "Update_flows_incrementally", log.Fields{"deviceId": device.Id, "flows": flows, "flowMetadata": flowMetadata})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ return handler.UpdateFlowsIncrementally(ctx, device, flows, groups, flowMetadata)
+ }
+ return olterrors.NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil)
+}
+
+//Update_pm_config returns PmConfigs nil or error
+func (oo *OpenOLT) Update_pm_config(ctx context.Context, device *voltha.Device, pmConfigs *voltha.PmConfigs) error {
+ logger.Debugw(ctx, "Update_pm_config", log.Fields{"device-id": device.Id, "pm-configs": pmConfigs})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ handler.UpdatePmConfig(ctx, pmConfigs)
+ return nil
+ }
+ return olterrors.NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil)
+}
+
+//Receive_packet_out sends packet out to the device
+func (oo *OpenOLT) Receive_packet_out(ctx context.Context, deviceID string, egressPortNo int, packet *openflow_13.OfpPacketOut) error {
+ logger.Debugw(ctx, "Receive_packet_out", log.Fields{"deviceId": deviceID, "egress_port_no": egressPortNo, "pkt": packet})
+ if handler := oo.getDeviceHandler(deviceID); handler != nil {
+ return handler.PacketOut(ctx, egressPortNo, packet)
+ }
+ return olterrors.NewErrNotFound("device-handler", log.Fields{"device-id": deviceID}, nil)
+}
+
+//Suppress_event unimplemented
+func (oo *OpenOLT) Suppress_event(ctx context.Context, filter *voltha.EventFilter) error {
+ return olterrors.ErrNotImplemented
+}
+
+//Unsuppress_event unimplemented
+func (oo *OpenOLT) Unsuppress_event(ctx context.Context, filter *voltha.EventFilter) error {
+ return olterrors.ErrNotImplemented
+}
+
+//Download_image unimplemented
+func (oo *OpenOLT) Download_image(ctx context.Context, device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, olterrors.ErrNotImplemented
+}
+
+//Get_image_download_status unimplemented
+func (oo *OpenOLT) Get_image_download_status(ctx context.Context, device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, olterrors.ErrNotImplemented
+}
+
+//Cancel_image_download unimplemented
+func (oo *OpenOLT) Cancel_image_download(ctx context.Context, device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, olterrors.ErrNotImplemented
+}
+
+//Activate_image_update unimplemented
+func (oo *OpenOLT) Activate_image_update(ctx context.Context, device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, olterrors.ErrNotImplemented
+}
+
+//Revert_image_update unimplemented
+func (oo *OpenOLT) Revert_image_update(ctx context.Context, device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, olterrors.ErrNotImplemented
+}
+
+// Enable_port to Enable PON/NNI interface
+func (oo *OpenOLT) Enable_port(ctx context.Context, deviceID string, port *voltha.Port) error {
+ logger.Infow(ctx, "Enable_port", log.Fields{"deviceId": deviceID, "port": port})
+ return oo.enableDisablePort(ctx, deviceID, port, true)
+}
+
+// Disable_port to Disable pon/nni interface
+func (oo *OpenOLT) Disable_port(ctx context.Context, deviceID string, port *voltha.Port) error {
+ logger.Infow(ctx, "Disable_port", log.Fields{"deviceId": deviceID, "port": port})
+ return oo.enableDisablePort(ctx, deviceID, port, false)
+}
+
+// enableDisablePort to Disable pon or Enable PON interface
+func (oo *OpenOLT) enableDisablePort(ctx context.Context, deviceID string, port *voltha.Port, enablePort bool) error {
+ logger.Infow(ctx, "enableDisablePort", log.Fields{"deviceId": deviceID, "port": port})
+ if port == nil {
+ return olterrors.NewErrInvalidValue(log.Fields{
+ "reason": "port cannot be nil",
+ "device-id": deviceID,
+ "port": nil}, nil)
+ }
+ if handler := oo.getDeviceHandler(deviceID); handler != nil {
+ logger.Debugw(ctx, "Enable_Disable_Port", log.Fields{"deviceId": deviceID, "port": port})
+ if enablePort {
+ if err := handler.EnablePort(ctx, port); err != nil {
+ return olterrors.NewErrAdapter("error-occurred-during-enable-port", log.Fields{"deviceID": deviceID, "port": port}, err)
+ }
+ } else {
+ if err := handler.DisablePort(ctx, port); err != nil {
+ return olterrors.NewErrAdapter("error-occurred-during-disable-port", log.Fields{"deviceID": deviceID, "port": port}, err)
+ }
+ }
+ }
+ return nil
+}
+
+//Child_device_lost deletes the ONU and its references from PONResources
+func (oo *OpenOLT) Child_device_lost(ctx context.Context, deviceID string, pPortNo uint32, onuID uint32) error {
+ logger.Infow(ctx, "Child-device-lost", log.Fields{"parentId": deviceID})
+ if handler := oo.getDeviceHandler(deviceID); handler != nil {
+ return handler.ChildDeviceLost(ctx, pPortNo, onuID)
+ }
+ return olterrors.NewErrNotFound("device-handler", log.Fields{"device-id": deviceID}, nil).Log()
+}
+
+//Start_omci_test not implemented
+func (oo *OpenOLT) Start_omci_test(ctx context.Context, device *voltha.Device, request *voltha.OmciTestRequest) (*voltha.TestResponse, error) {
+ return nil, olterrors.ErrNotImplemented
+}
+
+//Get_ext_value retrieves a value on a particular ONU
+func (oo *OpenOLT) Get_ext_value(ctx context.Context, deviceID string, device *voltha.Device, valueparam voltha.ValueType_Type) (*voltha.ReturnValues, error) {
+ var err error
+ resp := new(voltha.ReturnValues)
+ logger.Infow(ctx, "Get_ext_value", log.Fields{"device-id": deviceID, "onu-id": device.Id})
+ if handler := oo.getDeviceHandler(deviceID); handler != nil {
+ if resp, err = handler.getExtValue(ctx, device, valueparam); err != nil {
+ logger.Errorw(ctx, "error-occurred-during-get-ext-value", log.Fields{"device-id": deviceID, "onu-id": device.Id,
+ "error": err})
+ return nil, err
+ }
+ }
+ return resp, nil
+}
+
+func (oo *OpenOLT) Simulate_alarm(ctx context.Context, device *voltha.Device, request *voltha.SimulateAlarmRequest) error {
+ logger.Infow(ctx, "Simulate_alarm", log.Fields{"deviceId": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ logger.Infow(ctx, "Simulate_alarm", log.Fields{"request": request})
+ return handler.L2oamCmdRequest(ctx, device, request)
+ }
+ return olterrors.NewErrNotFound("device-handler", log.Fields{"device-id": device.Id}, nil)
+}
diff --git a/internal/pkg/core/openolt_eventmgr.go b/internal/pkg/core/openolt_eventmgr.go
new file mode 100644
index 0000000..f62f8a8
--- /dev/null
+++ b/internal/pkg/core/openolt_eventmgr.go
@@ -0,0 +1,788 @@
+/*
+ * 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 (
+ "context"
+ "errors"
+ "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-openolt-adapter/internal/pkg/olterrors"
+ "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"
+ 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"
+ onuRemoteDefectIndication = "ONU_REMOTE_DEFECT"
+ onuLossOfGEMChannelDelineationEvent = "ONU_LOSS_OF_GEM_CHANNEL_DELINEATION"
+ onuPhysicalEquipmentErrorEvent = "ONU_PHYSICAL_EQUIPMENT_ERROR"
+ onuLossOfAcknowledgementEvent = "ONU_LOSS_OF_ACKNOWLEDGEMENT"
+ onuDifferentialReachExceededEvent = "ONU_DIFFERENTIAL_REACH_EXCEEDED"
+)
+
+const (
+ statusCheckOn = "on"
+ statusCheckOff = "off"
+ operationStateUp = "up"
+ operationStateDown = "down"
+ base10 = 10
+)
+
+const (
+ ContextOltOperState = "oper-state"
+ ContextOnuOnuID = "onu-id"
+ ContextOnuPonIntfID = "intf-id"
+ ContextOnuSerialNumber = "serial-number"
+ ContextOnuDeviceID = "onu-device-id"
+ ContextOltPonIntfID = "intf-id"
+ ContextOnuFailureReaseon = "fail-reason"
+ ContextOnuDrift = "drift"
+ ContextOnuNewEqd = "new-eqd"
+ ContextOnuInverseBitErrorRate = "inverse-bit-error-rate"
+ ContextOltPonIntfOperState = "oper-state"
+ ContextOnuRemoteDefectIndicatorCount = "rdi-count"
+ ContextOnuDelineationErrors = "delineation-errors"
+ ContextOnuDifferentialDistance = "differential-distance"
+)
+
+// 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
+// nolint: gocyclo
+func (em *OpenOltEventMgr) ProcessEvents(ctx context.Context, alarmInd *oop.AlarmIndication, deviceID string, raisedTs int64) {
+ var err error
+ switch alarmInd.Data.(type) {
+ case *oop.AlarmIndication_LosInd:
+ logger.Debugw(ctx, "received-los-indication", log.Fields{"alarm-ind": alarmInd})
+ err = em.oltLosIndication(ctx, alarmInd.GetLosInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuAlarmInd:
+ logger.Debugw(ctx, "received-onu-alarm-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuAlarmIndication(ctx, alarmInd.GetOnuAlarmInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_DyingGaspInd:
+ logger.Debugw(ctx, "received-dying-gasp-indication", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuDyingGaspIndication(ctx, alarmInd.GetDyingGaspInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuActivationFailInd:
+ logger.Debugw(ctx, "received-onu-activation-fail-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuActivationFailIndication(ctx, alarmInd.GetOnuActivationFailInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuLossOmciInd:
+ logger.Debugw(ctx, "received-onu-loss-omci-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuLossOmciIndication(ctx, alarmInd.GetOnuLossOmciInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuDriftOfWindowInd:
+ logger.Debugw(ctx, "received-onu-drift-of-window-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuDriftOfWindowIndication(ctx, alarmInd.GetOnuDriftOfWindowInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuSignalDegradeInd:
+ logger.Debugw(ctx, "received-onu-signal-degrade-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuSignalDegradeIndication(ctx, alarmInd.GetOnuSignalDegradeInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuSignalsFailInd:
+ logger.Debugw(ctx, "received-onu-signal-fail-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuSignalsFailIndication(ctx, alarmInd.GetOnuSignalsFailInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuStartupFailInd:
+ logger.Debugw(ctx, "received-onu-startup-fail-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuStartupFailedIndication(ctx, alarmInd.GetOnuStartupFailInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuTiwiInd:
+ logger.Debugw(ctx, "received-onu-transmission-warning-indication ", log.Fields{"alarm-ind": alarmInd})
+ logger.Warnw(ctx, "not-implemented-yet", log.Fields{"alarm-ind": "Onu-Transmission-indication"})
+ case *oop.AlarmIndication_OnuLossOfSyncFailInd:
+ logger.Debugw(ctx, "received-onu-loss-of-sync-fail-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuLossOfSyncIndication(ctx, alarmInd.GetOnuLossOfSyncFailInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuItuPonStatsInd:
+ logger.Debugw(ctx, "received-onu-itu-pon-stats-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuItuPonStatsIndication(ctx, alarmInd.GetOnuItuPonStatsInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuDeactivationFailureInd:
+ logger.Debugw(ctx, "received-onu-deactivation-failure-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuDeactivationFailureIndication(ctx, alarmInd.GetOnuDeactivationFailureInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuLossGemDelineationInd:
+ logger.Debugw(ctx, "received-onu-loss-of-gem-channel-delineation-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuLossOfGEMChannelDelineationIndication(ctx, alarmInd.GetOnuLossGemDelineationInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuPhysicalEquipmentErrorInd:
+ logger.Debugw(ctx, "received-onu-physical-equipment-error-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuPhysicalEquipmentErrorIndication(ctx, alarmInd.GetOnuPhysicalEquipmentErrorInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuLossOfAckInd:
+ logger.Debugw(ctx, "received-onu-loss-of-acknowledgement-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuLossOfAcknowledgementIndication(ctx, alarmInd.GetOnuLossOfAckInd(), deviceID, raisedTs)
+ case *oop.AlarmIndication_OnuDiffReachExceededInd:
+ logger.Debugw(ctx, "received-onu-differential-reach-exceeded-indication ", log.Fields{"alarm-ind": alarmInd})
+ err = em.onuDifferentialReachExceededIndication(ctx, alarmInd.GetOnuDiffReachExceededInd(), deviceID, raisedTs)
+ default:
+ err = olterrors.NewErrInvalidValue(log.Fields{"indication-type": alarmInd}, nil)
+ }
+ if err != nil {
+ _ = olterrors.NewErrCommunication("publish-message", log.Fields{"indication-type": alarmInd}, err).LogAt(log.WarnLevel)
+ }
+}
+
+// oltUpDownIndication handles Up and Down state of an OLT
+func (em *OpenOltEventMgr) oltUpDownIndication(ctx context.Context, oltIndication *oop.OltIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context[ContextOltOperState] = 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(ctx, &de, voltha.EventCategory_COMMUNICATION, voltha.EventSubCategory_OLT, raisedTs); err != nil {
+ return olterrors.NewErrCommunication("send-olt-event", log.Fields{"device-id": deviceID}, err)
+ }
+ logger.Debugw(ctx, "olt-updown-event-sent-to-kafka", log.Fields{})
+ return nil
+}
+
+// OnuDiscoveryIndication is an exported method to handle ONU discovery event
+func (em *OpenOltEventMgr) OnuDiscoveryIndication(ctx context.Context, onuDisc *oop.OnuDiscIndication, oltDeviceID string, onuDeviceID string, OnuID uint32, serialNumber string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context[ContextOnuOnuID] = strconv.FormatUint(uint64(OnuID), base10)
+ context[ContextOnuPonIntfID] = strconv.FormatUint(uint64(onuDisc.IntfId), base10)
+ context[ContextOnuSerialNumber] = serialNumber
+ context[ContextOnuDeviceID] = onuDeviceID
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = oltDeviceID
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuDiscoveryEvent, "RAISE_EVENT")
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(ctx, &de, voltha.EventCategory_EQUIPMENT, voltha.EventSubCategory_PON, raisedTs); err != nil {
+ return olterrors.NewErrCommunication("send-onu-discovery-event",
+ log.Fields{
+ "serial-number": serialNumber,
+ "intf-id": onuDisc.IntfId}, err)
+ }
+ logger.Debugw(ctx, "onu-discovery-event-sent-to-kafka",
+ log.Fields{
+ "serial-number": serialNumber,
+ "intf-id": onuDisc.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) oltLosIndication(ctx context.Context, oltLos *oop.LosIndication, deviceID string, raisedTs int64) error {
+ var err error = nil
+ var de voltha.DeviceEvent
+ var alarmInd oop.OnuAlarmIndication
+ ponIntdID := PortNoToIntfID(oltLos.IntfId, voltha.Port_PON_OLT)
+
+ context := make(map[string]string)
+ /* Populating event context */
+ context[ContextOltPonIntfID] = 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")
+
+ /* When PON cable disconnected from OLT, it was expected OnuAlarmIndication
+ with "los_status: on" should be raised for each Onu connected to the PON
+ but BAL does not raise this Alarm hence manually sending OnuLosRaise event
+ for all the ONU's connected to PON on receiving LoSIndication for PON */
+ em.handler.onus.Range(func(Onukey interface{}, onuInCache interface{}) bool {
+ if onuInCache.(*OnuDevice).intfID == ponIntdID {
+ alarmInd.IntfId = ponIntdID
+ alarmInd.OnuId = onuInCache.(*OnuDevice).onuID
+ alarmInd.LosStatus = statusCheckOn
+ err = em.onuAlarmIndication(ctx, &alarmInd, deviceID, raisedTs)
+ }
+ return true
+ })
+ if err != nil {
+ /* Return if any error encountered while processing ONU LoS Event*/
+ return err
+ }
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", oltLosEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(ctx, &de, voltha.EventCategory_COMMUNICATION, voltha.EventSubCategory_OLT, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "olt-los-event-sent-to-kafka", log.Fields{"intf-id": oltLos.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) populateContextWithSerialDeviceID(context map[string]string, intfID, onuID uint32) {
+ var serialNumber = ""
+ var onuDeviceID = ""
+ onu := em.handler.formOnuKey(intfID, onuID)
+ if onu, ok := em.handler.onus.Load(onu); ok {
+ serialNumber = onu.(*OnuDevice).serialNumber
+ onuDeviceID = onu.(*OnuDevice).deviceID
+ }
+
+ context[ContextOnuSerialNumber] = serialNumber
+ context[ContextOnuDeviceID] = onuDeviceID
+}
+
+func (em *OpenOltEventMgr) onuDyingGaspIndication(ctx context.Context, dgi *oop.DyingGaspIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ em.populateContextWithSerialDeviceID(context, dgi.IntfId, dgi.OnuId)
+
+ context[ContextOnuPonIntfID] = strconv.FormatUint(uint64(dgi.IntfId), base10)
+ context[ContextOnuOnuID] = 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(ctx, &de, voltha.EventCategory_COMMUNICATION, voltha.EventSubCategory_PON, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-dying-gasp-event-sent-to-kafka", log.Fields{"intf-id": dgi.IntfId})
+ return nil
+}
+
+//wasLosRaised checks whether los raised already. If already raised returns true else false
+func (em *OpenOltEventMgr) wasLosRaised(ctx context.Context, onuAlarm *oop.OnuAlarmIndication) bool {
+ onuKey := em.handler.formOnuKey(onuAlarm.IntfId, onuAlarm.OnuId)
+ if onuInCache, ok := em.handler.onus.Load(onuKey); ok {
+ logger.Debugw(ctx, "onu-device-found-in-cache.", log.Fields{"intfID": onuAlarm.IntfId, "onuID": onuAlarm.OnuId})
+
+ if onuAlarm.LosStatus == statusCheckOn {
+ if onuInCache.(*OnuDevice).losRaised {
+ logger.Warnw(ctx, "onu-los-raised-already", log.Fields{"onu_id": onuAlarm.OnuId,
+ "intf_id": onuAlarm.IntfId, "LosStatus": onuAlarm.LosStatus})
+ return true
+ }
+ return false
+ }
+ }
+ return true
+}
+
+//wasLosCleared checks whether los cleared already. If already cleared returns true else false
+func (em *OpenOltEventMgr) wasLosCleared(ctx context.Context, onuAlarm *oop.OnuAlarmIndication) bool {
+ onuKey := em.handler.formOnuKey(onuAlarm.IntfId, onuAlarm.OnuId)
+ if onuInCache, ok := em.handler.onus.Load(onuKey); ok {
+ logger.Debugw(ctx, "onu-device-found-in-cache.", log.Fields{"intfID": onuAlarm.IntfId, "onuID": onuAlarm.OnuId})
+
+ if onuAlarm.LosStatus == statusCheckOff {
+ if !onuInCache.(*OnuDevice).losRaised {
+ logger.Warnw(ctx, "onu-los-cleared-already", log.Fields{"onu_id": onuAlarm.OnuId,
+ "intf_id": onuAlarm.IntfId, "LosStatus": onuAlarm.LosStatus})
+ return true
+ }
+ return false
+ }
+ }
+ return true
+}
+
+func (em *OpenOltEventMgr) getDeviceEventName(onuAlarm *oop.OnuAlarmIndication) string {
+ var deviceEventName string
+ if onuAlarm.LosStatus == statusCheckOn {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLosEvent, "RAISE_EVENT")
+ } else if onuAlarm.LosStatus == statusCheckOff {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLosEvent, "CLEAR_EVENT")
+ } else if onuAlarm.LobStatus == statusCheckOn {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLobEvent, "RAISE_EVENT")
+ } else if onuAlarm.LobStatus == statusCheckOff {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLobEvent, "CLEAR_EVENT")
+ } else if onuAlarm.LopcMissStatus == statusCheckOn {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLopcMissEvent, "RAISE_EVENT")
+ } else if onuAlarm.LopcMissStatus == statusCheckOff {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLopcMissEvent, "CLEAR_EVENT")
+ } else if onuAlarm.LopcMicErrorStatus == statusCheckOn {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLopcMicErrorEvent, "RAISE_EVENT")
+ } else if onuAlarm.LopcMicErrorStatus == statusCheckOff {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLopcMicErrorEvent, "CLEAR_EVENT")
+ } else if onuAlarm.LofiStatus == statusCheckOn {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLossOfFrameEvent, "RAISE_EVENT")
+ } else if onuAlarm.LofiStatus == statusCheckOff {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLossOfFrameEvent, "CLEAR_EVENT")
+ } else if onuAlarm.LoamiStatus == statusCheckOn {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLossOfPloamEvent, "RAISE_EVENT")
+ } else if onuAlarm.LoamiStatus == statusCheckOff {
+ deviceEventName = fmt.Sprintf("%s_%s", onuLossOfPloamEvent, "CLEAR_EVENT")
+ }
+ return deviceEventName
+}
+
+func (em *OpenOltEventMgr) onuAlarmIndication(ctx context.Context, onuAlarm *oop.OnuAlarmIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+
+ context := make(map[string]string)
+ /* Populating event context */
+ context[ContextOnuPonIntfID] = strconv.FormatUint(uint64(onuAlarm.IntfId), base10)
+ context[ContextOnuOnuID] = strconv.FormatUint(uint64(onuAlarm.OnuId), base10)
+ em.populateContextWithSerialDeviceID(context, onuAlarm.IntfId, onuAlarm.OnuId)
+
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ de.DeviceEventName = em.getDeviceEventName(onuAlarm)
+
+ switch onuAlarm.LosStatus {
+ case statusCheckOn:
+ if em.wasLosRaised(ctx, onuAlarm) {
+ /* No need to raise Onu Los Event as it might have already raised
+ or Onu might have deleted */
+ return nil
+ }
+ onuKey := em.handler.formOnuKey(onuAlarm.IntfId, onuAlarm.OnuId)
+ if onuInCache, ok := em.handler.onus.Load(onuKey); ok {
+ /* Update onu device with LoS raised state as true */
+ em.handler.onus.Store(onuKey, NewOnuDevice(onuInCache.(*OnuDevice).deviceID, onuInCache.(*OnuDevice).deviceType,
+ onuInCache.(*OnuDevice).serialNumber, onuInCache.(*OnuDevice).onuID, onuInCache.(*OnuDevice).intfID,
+ onuInCache.(*OnuDevice).proxyDeviceID, true))
+ }
+ case statusCheckOff:
+ if em.wasLosCleared(ctx, onuAlarm) {
+ /* No need to clear Onu Los Event as it might have already cleared
+ or Onu might have deleted */
+ return nil
+ }
+ onuKey := em.handler.formOnuKey(onuAlarm.IntfId, onuAlarm.OnuId)
+ if onuInCache, ok := em.handler.onus.Load(onuKey); ok {
+ /* Update onu device with LoS raised state as false */
+ em.handler.onus.Store(onuKey, NewOnuDevice(onuInCache.(*OnuDevice).deviceID, onuInCache.(*OnuDevice).deviceType,
+ onuInCache.(*OnuDevice).serialNumber, onuInCache.(*OnuDevice).onuID, onuInCache.(*OnuDevice).intfID,
+ onuInCache.(*OnuDevice).proxyDeviceID, false))
+ }
+ }
+
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(ctx, &de, voltha.EventCategory_COMMUNICATION, voltha.EventSubCategory_ONU, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-los-event-sent-to-kafka", log.Fields{"onu-id": onuAlarm.OnuId, "intf-id": onuAlarm.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuActivationFailIndication(ctx context.Context, oaf *oop.OnuActivationFailureIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context[ContextOnuPonIntfID] = strconv.FormatUint(uint64(oaf.IntfId), base10)
+ context[ContextOnuOnuID] = strconv.FormatUint(uint64(oaf.OnuId), base10)
+ context[ContextOnuFailureReaseon] = strconv.FormatUint(uint64(oaf.FailReason), base10)
+
+ em.populateContextWithSerialDeviceID(context, oaf.IntfId, oaf.OnuId)
+
+ /* 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(ctx, &de, voltha.EventCategory_EQUIPMENT, voltha.EventSubCategory_PON, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-activation-failure-event-sent-to-kafka", log.Fields{"onu-id": oaf.OnuId, "intf-id": oaf.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuLossOmciIndication(ctx context.Context, onuLossOmci *oop.OnuLossOfOmciChannelIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context[ContextOnuPonIntfID] = strconv.FormatUint(uint64(onuLossOmci.IntfId), base10)
+ context[ContextOnuOnuID] = strconv.FormatUint(uint64(onuLossOmci.OnuId), base10)
+
+ em.populateContextWithSerialDeviceID(context, onuLossOmci.IntfId, onuLossOmci.OnuId)
+
+ /* 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(ctx, &de, voltha.EventCategory_COMMUNICATION, voltha.EventSubCategory_PON, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "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(ctx context.Context, onuDriftWindow *oop.OnuDriftOfWindowIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context[ContextOnuPonIntfID] = strconv.FormatUint(uint64(onuDriftWindow.IntfId), base10)
+ context[ContextOnuOnuID] = strconv.FormatUint(uint64(onuDriftWindow.OnuId), base10)
+ context[ContextOnuDrift] = strconv.FormatUint(uint64(onuDriftWindow.Drift), base10)
+ context[ContextOnuNewEqd] = strconv.FormatUint(uint64(onuDriftWindow.NewEqd), base10)
+
+ em.populateContextWithSerialDeviceID(context, onuDriftWindow.IntfId, onuDriftWindow.OnuId)
+
+ /* 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(ctx, &de, voltha.EventCategory_COMMUNICATION, voltha.EventSubCategory_PON, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-drift-of-window-event-sent-to-kafka", log.Fields{"onu-id": onuDriftWindow.OnuId, "intf-id": onuDriftWindow.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuSignalDegradeIndication(ctx context.Context, onuSignalDegrade *oop.OnuSignalDegradeIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ context[ContextOnuPonIntfID] = strconv.FormatUint(uint64(onuSignalDegrade.IntfId), base10)
+ context[ContextOnuOnuID] = strconv.FormatUint(uint64(onuSignalDegrade.OnuId), base10)
+ context[ContextOnuInverseBitErrorRate] = strconv.FormatUint(uint64(onuSignalDegrade.InverseBitErrorRate), base10)
+
+ em.populateContextWithSerialDeviceID(context, onuSignalDegrade.IntfId, onuSignalDegrade.OnuId)
+
+ /* 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(ctx, &de, voltha.EventCategory_COMMUNICATION, voltha.EventSubCategory_PON, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-signal-degrade-event-sent-to-kafka", log.Fields{"onu-id": onuSignalDegrade.OnuId, "intf-id": onuSignalDegrade.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuSignalsFailIndication(ctx context.Context, onuSignalsFail *oop.OnuSignalsFailureIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ em.populateContextWithSerialDeviceID(context, onuSignalsFail.IntfId, onuSignalsFail.OnuId)
+
+ context[ContextOnuOnuID] = strconv.FormatUint(uint64(onuSignalsFail.OnuId), base10)
+ context[ContextOnuPonIntfID] = strconv.FormatUint(uint64(onuSignalsFail.IntfId), base10)
+ context[ContextOnuInverseBitErrorRate] = 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(ctx, &de, voltha.EventCategory_COMMUNICATION, voltha.EventSubCategory_PON, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-signals-fail-event-sent-to-kafka", log.Fields{"onu-id": onuSignalsFail.OnuId, "intf-id": onuSignalsFail.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuStartupFailedIndication(ctx context.Context, onuStartupFail *oop.OnuStartupFailureIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ em.populateContextWithSerialDeviceID(context, onuStartupFail.IntfId, onuStartupFail.OnuId)
+
+ context[ContextOnuOnuID] = strconv.FormatUint(uint64(onuStartupFail.OnuId), base10)
+ context[ContextOnuPonIntfID] = 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(ctx, &de, voltha.EventCategory_COMMUNICATION, voltha.EventSubCategory_PON, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-startup-fail-event-sent-to-kafka", log.Fields{"onu-id": onuStartupFail.OnuId, "intf-id": onuStartupFail.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuLossOfSyncIndication(ctx context.Context, onuLOKI *oop.OnuLossOfKeySyncFailureIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ em.populateContextWithSerialDeviceID(context, onuLOKI.IntfId, onuLOKI.OnuId)
+
+ context[ContextOnuOnuID] = strconv.FormatUint(uint64(onuLOKI.OnuId), base10)
+ context[ContextOnuPonIntfID] = 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(ctx, &de, voltha.EventCategory_SECURITY, voltha.EventSubCategory_ONU, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "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(ctx context.Context, ifindication *oop.IntfOperIndication, deviceID string, raisedTs int64) {
+ portNo := IntfIDToPortNo(ifindication.IntfId, voltha.Port_PON_OLT)
+ if port, err := em.handler.coreProxy.GetDevicePort(ctx, deviceID, portNo); err != nil {
+ logger.Warnw(ctx, "Error while fetching port object", log.Fields{"device-id": deviceID, "error": err})
+ } else if port.AdminState != common.AdminState_ENABLED {
+ logger.Debugw(ctx, "port-disable/enable-event-not-generated--the-port-is-not-enabled-by-operator", log.Fields{"device-id": deviceID, "port": port})
+ return
+ }
+ /* Populating event context */
+ context := map[string]string{ContextOltPonIntfOperState: ifindication.GetOperState()}
+ /* Populating device event body */
+ var de voltha.DeviceEvent
+ 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(ctx, &de, voltha.EventCategory_COMMUNICATION, voltha.EventSubCategory_OLT, raisedTs); err != nil {
+ _ = olterrors.NewErrCommunication("send-olt-intf-oper-status-event", log.Fields{"device-id": deviceID, "intf-id": ifindication.IntfId, "oper-state": ifindication.OperState}, err).LogAt(log.WarnLevel)
+ return
+ }
+ logger.Debug(ctx, "sent-olt-intf-oper-status-event-to-kafka")
+}
+
+func (em *OpenOltEventMgr) onuDeactivationFailureIndication(ctx context.Context, onuDFI *oop.OnuDeactivationFailureIndication, deviceID string, raisedTs int64) error {
+ var de voltha.DeviceEvent
+ context := make(map[string]string)
+ /* Populating event context */
+ em.populateContextWithSerialDeviceID(context, onuDFI.IntfId, onuDFI.OnuId)
+
+ context[ContextOnuOnuID] = strconv.FormatUint(uint64(onuDFI.OnuId), base10)
+ context[ContextOnuPonIntfID] = strconv.FormatUint(uint64(onuDFI.IntfId), base10)
+ /* Populating device event body */
+ de.Context = context
+ de.ResourceId = deviceID
+ if onuDFI.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuDeactivationFailureEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuDeactivationFailureEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(ctx, &de, voltha.EventCategory_EQUIPMENT, voltha.EventSubCategory_ONU, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-deactivation-failure-event-sent-to-kafka", log.Fields{"onu-id": onuDFI.OnuId, "intf-id": onuDFI.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuRemoteDefectIndication(ctx context.Context, onuID uint32, intfID uint32, rdiCount uint64, status string, deviceID string, raisedTs int64) error {
+ /* Populating event context */
+ context := map[string]string{
+ ContextOnuOnuID: strconv.FormatUint(uint64(onuID), base10),
+ ContextOnuPonIntfID: strconv.FormatUint(uint64(intfID), base10),
+ ContextOnuRemoteDefectIndicatorCount: strconv.FormatUint(rdiCount, base10),
+ }
+ em.populateContextWithSerialDeviceID(context, intfID, onuID)
+
+ /* Populating device event body */
+ de := &voltha.DeviceEvent{
+ Context: context,
+ ResourceId: deviceID,
+ }
+ if status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuRemoteDefectIndication, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuRemoteDefectIndication, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(ctx, de, voltha.EventCategory_EQUIPMENT, voltha.EventSubCategory_ONU, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-remote-defect-event-sent-to-kafka", log.Fields{"onu-id": onuID, "intf-id": intfID})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuItuPonStatsIndication(ctx context.Context, onuIPS *oop.OnuItuPonStatsIndication, deviceID string, raisedTs int64) error {
+ onuDevice, found := em.handler.onus.Load(em.handler.formOnuKey(onuIPS.IntfId, onuIPS.OnuId))
+ if !found {
+ return errors.New("unknown-onu-device")
+ }
+ if onuIPS.GetRdiErrorInd().Status == statusCheckOn {
+ if !onuDevice.(*OnuDevice).rdiRaised {
+ if err := em.onuRemoteDefectIndication(ctx, onuIPS.OnuId, onuIPS.IntfId, onuIPS.GetRdiErrorInd().RdiErrorCount, statusCheckOn, deviceID, raisedTs); err != nil {
+ return err
+ }
+ onuDevice.(*OnuDevice).rdiRaised = true
+ return nil
+ }
+ logger.Debugw(ctx, "onu-remote-defect-already-raised", log.Fields{"onu-id": onuIPS.OnuId, "intf-id": onuIPS.IntfId})
+ } else {
+ if err := em.onuRemoteDefectIndication(ctx, onuIPS.OnuId, onuIPS.IntfId, onuIPS.GetRdiErrorInd().RdiErrorCount, statusCheckOff, deviceID, raisedTs); err != nil {
+ return err
+ }
+ onuDevice.(*OnuDevice).rdiRaised = false
+ }
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuLossOfGEMChannelDelineationIndication(ctx context.Context, onuGCD *oop.OnuLossOfGEMChannelDelineationIndication, deviceID string, raisedTs int64) error {
+ /* Populating event context */
+ context := map[string]string{
+ ContextOnuOnuID: strconv.FormatUint(uint64(onuGCD.OnuId), base10),
+ ContextOnuPonIntfID: strconv.FormatUint(uint64(onuGCD.IntfId), base10),
+ ContextOnuDelineationErrors: strconv.FormatUint(uint64(onuGCD.DelineationErrors), base10),
+ }
+ em.populateContextWithSerialDeviceID(context, onuGCD.IntfId, onuGCD.OnuId)
+
+ /* Populating device event body */
+ de := &voltha.DeviceEvent{
+ Context: context,
+ ResourceId: deviceID,
+ }
+ if onuGCD.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOfGEMChannelDelineationEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOfGEMChannelDelineationEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(ctx, de, voltha.EventCategory_COMMUNICATION, voltha.EventSubCategory_ONU, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-loss-of-gem-channel-delineation-event-sent-to-kafka", log.Fields{"onu-id": onuGCD.OnuId, "intf-id": onuGCD.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuPhysicalEquipmentErrorIndication(ctx context.Context, onuErr *oop.OnuPhysicalEquipmentErrorIndication, deviceID string, raisedTs int64) error {
+ /* Populating event context */
+ context := map[string]string{
+ ContextOnuOnuID: strconv.FormatUint(uint64(onuErr.OnuId), base10),
+ ContextOnuPonIntfID: strconv.FormatUint(uint64(onuErr.IntfId), base10),
+ }
+ em.populateContextWithSerialDeviceID(context, onuErr.IntfId, onuErr.OnuId)
+ /* Populating device event body */
+ de := &voltha.DeviceEvent{
+ Context: context,
+ ResourceId: deviceID,
+ }
+ if onuErr.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuPhysicalEquipmentErrorEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuPhysicalEquipmentErrorEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(ctx, de, voltha.EventCategory_EQUIPMENT, voltha.EventSubCategory_ONU, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-physical-equipment-error-event-sent-to-kafka", log.Fields{"onu-id": onuErr.OnuId, "intf-id": onuErr.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuLossOfAcknowledgementIndication(ctx context.Context, onuLOA *oop.OnuLossOfAcknowledgementIndication, deviceID string, raisedTs int64) error {
+ /* Populating event context */
+ context := map[string]string{
+ ContextOnuOnuID: strconv.FormatUint(uint64(onuLOA.OnuId), base10),
+ ContextOnuPonIntfID: strconv.FormatUint(uint64(onuLOA.IntfId), base10),
+ }
+ em.populateContextWithSerialDeviceID(context, onuLOA.IntfId, onuLOA.OnuId)
+
+ /* Populating device event body */
+ de := &voltha.DeviceEvent{
+ Context: context,
+ ResourceId: deviceID,
+ }
+ if onuLOA.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOfAcknowledgementEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuLossOfAcknowledgementEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(ctx, de, voltha.EventCategory_EQUIPMENT, voltha.EventSubCategory_ONU, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-physical-equipment-error-event-sent-to-kafka", log.Fields{"onu-id": onuLOA.OnuId, "intf-id": onuLOA.IntfId})
+ return nil
+}
+
+func (em *OpenOltEventMgr) onuDifferentialReachExceededIndication(ctx context.Context, onuDRE *oop.OnuDifferentialReachExceededIndication, deviceID string, raisedTs int64) error {
+ /* Populating event context */
+ context := map[string]string{
+ ContextOnuOnuID: strconv.FormatUint(uint64(onuDRE.OnuId), base10),
+ ContextOnuPonIntfID: strconv.FormatUint(uint64(onuDRE.IntfId), base10),
+ ContextOnuDifferentialDistance: strconv.FormatUint(uint64(onuDRE.Distance), base10),
+ }
+ em.populateContextWithSerialDeviceID(context, onuDRE.IntfId, onuDRE.OnuId)
+
+ /* Populating device event body */
+ de := &voltha.DeviceEvent{
+ Context: context,
+ ResourceId: deviceID,
+ }
+ if onuDRE.Status == statusCheckOn {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuDifferentialReachExceededEvent, "RAISE_EVENT")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", onuDifferentialReachExceededEvent, "CLEAR_EVENT")
+ }
+ /* Send event to KAFKA */
+ if err := em.eventProxy.SendDeviceEvent(ctx, de, voltha.EventCategory_EQUIPMENT, voltha.EventSubCategory_ONU, raisedTs); err != nil {
+ return err
+ }
+ logger.Debugw(ctx, "onu-differential-reach-exceeded–event-sent-to-kafka", log.Fields{"onu-id": onuDRE.OnuId, "intf-id": onuDRE.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..84dff0b
--- /dev/null
+++ b/internal/pkg/core/openolt_eventmgr_test.go
@@ -0,0 +1,166 @@
+/*
+ * 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 (
+ "context"
+ "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 := newMockDeviceHandler()
+ 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})
+ dh.onus.Store("1.3", NewOnuDevice("onu3", "onu3", "onu3", 1, 3, "onu3", false))
+ dh.onus.Store("1.4", NewOnuDevice("onu4", "onu4", "onu4", 1, 4, "onu4", false))
+ 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
+ /* Pon Interface ID from Openolt agent is sent as port no while raising LoSIndication hence following same in test to have similar behavior
+ 0x2 << 28 ^ 536870913 = 1 --> pon Intf Id*/
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_LosInd{LosInd: &oop.LosIndication{IntfId: 536870913, Status: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_LosInd{LosInd: &oop.LosIndication{IntfId: 536870913}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_LosInd{LosInd: &oop.LosIndication{IntfId: 536870913, 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()}},
+ // Duplicate test to get onu los already cleared result
+ {"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()}},
+
+ // AlarmIndication_onuDeactivationFailureInd
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuDeactivationFailureInd{OnuDeactivationFailureInd: &oop.OnuDeactivationFailureIndication{IntfId: 1, OnuId: 3, Status: "on"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuDeactivationFailureInd{OnuDeactivationFailureInd: &oop.OnuDeactivationFailureIndication{IntfId: 1, OnuId: 3, Status: "off"}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ // AlarmIndication_onuItuPonStatsIndication
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuItuPonStatsInd{OnuItuPonStatsInd: &oop.OnuItuPonStatsIndication{IntfId: 1, OnuId: 3, Stats: &oop.OnuItuPonStatsIndication_RdiErrorInd{RdiErrorInd: &oop.RdiErrorIndication{RdiErrorCount: 4, Status: "on"}}}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ {"ProcessEvents-", args{alarmInd: &oop.AlarmIndication{Data: &oop.AlarmIndication_OnuItuPonStatsInd{OnuItuPonStatsInd: &oop.OnuItuPonStatsIndication{IntfId: 1, OnuId: 3, Stats: &oop.OnuItuPonStatsIndication_RdiErrorInd{RdiErrorInd: &oop.RdiErrorIndication{RdiErrorCount: 0, Status: "off"}}}}}, deviceID: "olt", raisedTs: time.Now().Unix()}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ em.ProcessEvents(context.Background(), tt.args.alarmInd, tt.args.deviceID, tt.args.raisedTs)
+ })
+ }
+}
+
+func TestOpenOltEventMgr_OnuDiscoveryIndication(t *testing.T) {
+ em := mockEventMgr()
+ type args struct {
+ onuDisc *oop.OnuDiscIndication
+ oltDeviceID string
+ onuDeviceID 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")}}, oltDeviceID: "olt", onuDeviceID: "onu1", 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(context.Background(), tt.args.onuDisc, tt.args.oltDeviceID, tt.args.onuDeviceID, tt.args.OnuID, tt.args.serialNumber, tt.args.raisedTs)
+ //TODO: actually verify test cases
+ })
+ }
+}
diff --git a/internal/pkg/core/openolt_flowmgr.go b/internal/pkg/core/openolt_flowmgr.go
new file mode 100644
index 0000000..449afe8
--- /dev/null
+++ b/internal/pkg/core/openolt_flowmgr.go
@@ -0,0 +1,4119 @@
+/*
+ * 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 (
+ "context"
+ "crypto/md5"
+ "encoding/binary"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "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"
+
+ "github.com/EagleChen/mapmutex"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/olterrors"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+const (
+
+ HsiaFlow = "HSIA_FLOW"
+
+ EapolFlow = "EAPOL_FLOW"
+
+ DhcpFlow = "DHCP_FLOW"
+
+ MulticastFlow = "MULTICAST_FLOW"
+
+ IgmpFlow = "IGMP_FLOW"
+
+ IPProtoDhcp = 17
+
+ IPProtoIgmp = 2
+
+ EapEthType = 0x888e
+ LldpEthType = 0x88cc
+ IPv4EthType = 0x800
+
+ IgmpProto = 2
+
+ ReservedVlan = 4096
+
+ DefaultMgmtVlan = 4091
+
+
+ Upstream = "upstream"
+ Downstream = "downstream"
+ Multicast = "multicast"
+ PacketTagType = "pkt_tag_type"
+ Untagged = "untagged"
+ SingleTag = "single_tag"
+ DoubleTag = "double_tag"
+
+
+ EthType = "eth_type"
+ EthDst = "eth_dst"
+ TPID = "tpid"
+ IPProto = "ip_proto"
+ InPort = "in_port"
+ VlanVid = "vlan_vid"
+ VlanPcp = "vlan_pcp"
+
+ UDPDst = "udp_dst"
+ UDPSrc = "udp_src"
+ Ipv4Dst = "ipv4_dst"
+ Ipv4Src = "ipv4_src"
+ Metadata = "metadata"
+ TunnelID = "tunnel_id"
+ Output = "output"
+ GroupID = "group_id"
+
+ PopVlan = "pop_vlan"
+ PushVlan = "push_vlan"
+ TrapToHost = "trap_to_host"
+ MaxMeterBand = 2
+ VlanPCPMask = 0xFF
+ VlanvIDMask = 0xFFF
+ IntfID = "intfId"
+ OnuID = "onuId"
+ UniID = "uniId"
+ PortNo = "portNo"
+ AllocID = "allocId"
+
+ NoneOnuID = -1
+ NoneUniID = -1
+ NoneGemPortID = -1
+
+ BinaryStringPrefix = "0b"
+ BinaryBit1 = '1'
+
+ maxRetry = 300
+ maxDelay = 100000000
+ baseDelay = 10000000
+ factor = 1.1
+ jitter = 0.2
+)
+
+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 interface{}
+ 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
+ perGemPortLock *mapmutex.Mutex // lock to be used to access the flowsUsedByGemPort map
+ 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, indexed by IntfId
+ onuGemInfoLock sync.RWMutex
+ pendingFlowDelete sync.Map
+ perUserFlowHandleLock *mapmutex.Mutex
+ interfaceToMcastQueueMap map[uint32]*queueInfoBrief /*pon interface -> multicast queue map. Required to assign GEM to a bucket during group population*/
+}
+
+var autoAddFlow bool = true
+var noticeDownloadRequest bool = false
+var mapCreateScheduler map[string]bool = map[string]bool{}
+var mapAddFlow map[string]bool = map[string]bool{}
+
+func resetFlowMap() {
+ mapCreateScheduler = map[string]bool{}
+ mapAddFlow = map[string]bool{}
+}
+
+//NewFlowManager creates OpenOltFlowMgr object and initializes the parameters
+func NewFlowManager(ctx context.Context, dh *DeviceHandler, rMgr *rsrcMgr.OpenOltResourceMgr) *OpenOltFlowMgr {
+ logger.Infow(ctx, "initializing-flow-manager", log.Fields{"device-id": dh.device.Id})
+ 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(ctx); err != nil {
+ logger.Errorw(ctx, "error-while-populating-tech-profile-mgr", log.Fields{"error": err})
+ return nil
+ }
+ flowMgr.onuIdsLock = sync.RWMutex{}
+ flowMgr.flowsUsedByGemPort = make(map[gemPortKey][]uint32)
+ flowMgr.packetInGemPort = make(map[rsrcMgr.PacketInInfoKey]uint32)
+ ponPorts := rMgr.DevInfo.GetPonPorts()
+ flowMgr.onuGemInfo = make(map[uint32][]rsrcMgr.OnuGemInfo, ponPorts)
+ for idx = 0; idx < ponPorts; idx++ {
+ if flowMgr.onuGemInfo[idx], err = rMgr.GetOnuGemInfo(ctx, idx); err != nil {
+ logger.Error(ctx, "failed-to-load-onu-gem-info-cache")
+ }
+ //Load flowID list per gem map per interface from the kvstore.
+ flowMgr.loadFlowIDlistForGem(ctx, idx)
+ }
+ flowMgr.onuGemInfoLock = sync.RWMutex{}
+ flowMgr.pendingFlowDelete = sync.Map{}
+ flowMgr.perUserFlowHandleLock = mapmutex.NewCustomizedMapMutex(maxRetry, maxDelay, baseDelay, factor, jitter)
+ flowMgr.perGemPortLock = mapmutex.NewCustomizedMapMutex(maxRetry, maxDelay, baseDelay, factor, jitter)
+ flowMgr.interfaceToMcastQueueMap = make(map[uint32]*queueInfoBrief)
+ flowMgr.loadInterfaceToMulticastQueueMap(ctx)
+ logger.Info(ctx, "initialization-of-flow-manager-success")
+ return &flowMgr
+}
+
+func (f *OpenOltFlowMgr) registerFlow(ctx context.Context, flowFromCore *ofp.OfpFlowStats, deviceFlow *openoltpb2.Flow) error {
+ gemPK := gemPortKey{uint32(deviceFlow.AccessIntfId), uint32(deviceFlow.GemportId)}
+ if f.perGemPortLock.TryLock(gemPK) {
+ logger.Debugw(ctx, "registering-flow-for-device ",
+ log.Fields{
+ "flow": flowFromCore,
+ "device-id": f.deviceHandler.device.Id})
+ flowIDList, ok := f.flowsUsedByGemPort[gemPK]
+ if !ok {
+ flowIDList = []uint32{deviceFlow.FlowId}
+ }
+ flowIDList = appendUnique(flowIDList, deviceFlow.FlowId)
+ f.flowsUsedByGemPort[gemPK] = flowIDList
+
+ f.perGemPortLock.Unlock(gemPK)
+
+ // update the flowids for a gem to the KVstore
+ return f.resourceMgr.UpdateFlowIDsForGem(ctx, uint32(deviceFlow.AccessIntfId), uint32(deviceFlow.GemportId), flowIDList)
+ }
+ logger.Error(ctx, "failed-to-acquire-per-gem-port-lock",
+ log.Fields{
+ "flow-from-core": flowFromCore,
+ "device-id": f.deviceHandler.device.Id,
+ "key": gemPK,
+ })
+ return olterrors.NewErrAdapter("failed-to-acquire-per-gem-port-lock", log.Fields{
+ "flow-from-core": flowFromCore,
+ "device-id": f.deviceHandler.device.Id,
+ "key": gemPK,
+ }, nil)
+}
+
+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 interface{}
+
+ logger.Infow(ctx, "dividing-flow", log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "port-no": portNo,
+ "classifier": classifierInfo,
+ "action": actionInfo,
+ "usmeter-iD": UsMeterID,
+ "dsmeter-iD": DsMeterID,
+ "tp-id": TpID})
+ if onuID == 0 {
+ logger.Errorw(ctx, "no-onu-id-for-flow",
+ log.Fields{
+ "port-no": portNo,
+ "classifer": classifierInfo,
+ "action": actionInfo,
+ "device-id": f.deviceHandler.device.Id})
+ return
+ }
+
+ uni := getUniPortPath(f.deviceHandler.device.Id, intfID, int32(onuID), int32(uniID))
+ logger.Debugw(ctx, "uni-port-path", log.Fields{
+ "uni": uni,
+ "device-id": f.deviceHandler.device.Id})
+
+ tpLockMapKey := tpLockKey{intfID, onuID, uniID}
+ if f.perUserFlowHandleLock.TryLock(tpLockMapKey) {
+ logger.Debugw(ctx, "dividing-flow-create-tcont-gem-ports", log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "port-no": portNo,
+ "classifier": classifierInfo,
+ "action": actionInfo,
+ "usmeter-id": UsMeterID,
+ "dsmeter-id": DsMeterID,
+ "tp-id": TpID})
+ allocID, gemPorts, TpInst = f.createTcontGemports(ctx, intfID, onuID, uniID, uni, portNo, TpID, UsMeterID, DsMeterID, flowMetadata)
+ if allocID == 0 || gemPorts == nil || TpInst == nil {
+ logger.Error(ctx, "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 {
+ logger.Errorw(ctx, "failed-to-acquire-per-user-flow-handle-lock",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "flow-id": flow.Id,
+ "flow-cookie": flow.Cookie,
+ "device-id": f.deviceHandler.device.Id})
+ 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 {
+
+ logger.Debugw(ctx, "CreateSchedulerQueues",
+ log.Fields{"dir": sq.direction,
+ "intf-id": sq.intfID,
+ "onu-id": sq.onuID,
+ "uni-id": sq.uniID,
+ "tp-id": sq.tpID,
+ "meter-id": sq.meterID,
+ "tp-inst": sq.tpInst,
+ "flowmetadata": sq.flowMetadata,
+ "device-id": f.deviceHandler.device.Id})
+
+ 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 {
+ return olterrors.NewErrNotFound("meter",
+ log.Fields{"intf-id": sq.intfID,
+ "onu-id": sq.onuID,
+ "uni-id": sq.uniID,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+
+ if KvStoreMeter != nil {
+ if KvStoreMeter.MeterId == sq.meterID {
+ logger.Debugw(ctx, "scheduler-already-created-for-upstream", log.Fields{"device-id": f.deviceHandler.device.Id})
+ return nil
+ }
+ return olterrors.NewErrInvalidValue(log.Fields{
+ "unsupported": "meter-id",
+ "kv-store-meter-id": KvStoreMeter.MeterId,
+ "meter-id-in-flow": sq.meterID,
+ "device-id": f.deviceHandler.device.Id}, nil)
+ }
+
+ logger.Debugw(ctx, "meter-does-not-exist-creating-new",
+ log.Fields{
+ "meter-id": sq.meterID,
+ "direction": Direction,
+ "device-id": f.deviceHandler.device.Id})
+
+ if sq.direction == tp_pb.Direction_UPSTREAM {
+ SchedCfg, err = f.techprofile[sq.intfID].GetUsScheduler(ctx, sq.tpInst.(*tp.TechProfile))
+ } else if sq.direction == tp_pb.Direction_DOWNSTREAM {
+ SchedCfg, err = f.techprofile[sq.intfID].GetDsScheduler(ctx, sq.tpInst.(*tp.TechProfile))
+ }
+
+ if err != nil {
+ return olterrors.NewErrNotFound("scheduler-config",
+ log.Fields{
+ "intf-id": sq.intfID,
+ "direction": sq.direction,
+ "tp-inst": sq.tpInst,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+
+ var meterConfig *ofp.OfpMeterConfig
+ if sq.flowMetadata != nil {
+ for _, meter := range sq.flowMetadata.Meters {
+ if sq.meterID == meter.MeterId {
+ meterConfig = meter
+ logger.Debugw(ctx, "found-meter-config-from-flowmetadata",
+ log.Fields{"meterConfig": meterConfig,
+ "device-id": f.deviceHandler.device.Id})
+ break
+ }
+ }
+ } else {
+ logger.Errorw(ctx, "flow-metadata-not-present-in-flow", log.Fields{"device-id": f.deviceHandler.device.Id})
+ }
+ if meterConfig == nil {
+ return olterrors.NewErrNotFound("meterbands", log.Fields{
+ "reason": "Could-not-get-meterbands-from-flowMetadata",
+ "flow-metadata": sq.flowMetadata,
+ "meter-id": sq.meterID,
+ "device-id": f.deviceHandler.device.Id}, nil)
+ } else if len(meterConfig.Bands) < MaxMeterBand {
+ logger.Errorw(ctx, "invalid-number-of-bands-in-meter",
+ log.Fields{"Bands": meterConfig.Bands,
+ "meter-id": sq.meterID,
+ "device-id": f.deviceHandler.device.Id})
+ return olterrors.NewErrInvalidValue(log.Fields{
+ "reason": "Invalid-number-of-bands-in-meter",
+ "meterband-count": len(meterConfig.Bands),
+ "metabands": meterConfig.Bands,
+ "meter-id": sq.meterID,
+ "device-id": f.deviceHandler.device.Id}, nil)
+ }
+ 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.(*tp.TechProfile), SchedCfg, TrafficShaping)}
+ TrafficSched[0].TechProfileId = sq.tpID
+
+ if err := f.pushSchedulerQueuesToDevice(ctx, sq, TrafficShaping, TrafficSched); err != nil {
+ return olterrors.NewErrAdapter("failure-pushing-traffic-scheduler-and-queues-to-device",
+ log.Fields{"intf-id": sq.intfID,
+ "direction": sq.direction,
+ "device-id": f.deviceHandler.device.Id}, 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 {
+ return olterrors.NewErrAdapter("failed-updating-meter-id",
+ log.Fields{"onu-id": sq.onuID,
+ "meter-id": sq.meterID,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+ logger.Infow(ctx, "updated-meter-info-into-kv-store-successfully",
+ log.Fields{"direction": Direction,
+ "Meter": meterConfig,
+ "device-id": f.deviceHandler.device.Id})
+
+ 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(ctx, sq.tpInst.(*tp.TechProfile), sq.direction)
+
+ if err != nil {
+ return olterrors.NewErrAdapter("unable-to-construct-traffic-queue-configuration",
+ log.Fields{"intf-id": sq.intfID,
+ "direction": sq.direction,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+
+ logger.Debugw(ctx, "sending-traffic-scheduler-create-to-device",
+ log.Fields{
+ "direction": sq.direction,
+ "TrafficScheds": TrafficSched,
+ "device-id": f.deviceHandler.device.Id})
+ 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 {
+ return olterrors.NewErrAdapter("failed-to-create-traffic-schedulers-in-device", log.Fields{"TrafficScheds": TrafficSched}, err)
+ }
+ logger.Infow(ctx, "successfully-created-traffic-schedulers", log.Fields{
+ "direction": sq.direction,
+ "traffic-queues": trafficQueues,
+ "device-id": f.deviceHandler.device.Id})
+
+ logger.Debugw(ctx, "sending-traffic-queues-create-to-device",
+ log.Fields{"direction": sq.direction,
+ "traffic-queues": trafficQueues,
+ "device-id": f.deviceHandler.device.Id})
+ if _, err := f.deviceHandler.Client.CreateTrafficQueues(ctx,
+ &tp_pb.TrafficQueues{IntfId: sq.intfID, OnuId: sq.onuID,
+ UniId: sq.uniID, PortNo: sq.uniPort,
+ TrafficQueues: trafficQueues,
+ TechProfileId: TrafficSched[0].TechProfileId}); err != nil {
+ return olterrors.NewErrAdapter("failed-to-create-traffic-queues-in-device", log.Fields{"traffic-queues": trafficQueues}, err)
+ }
+ logger.Infow(ctx, "successfully-created-traffic-schedulers", log.Fields{
+ "direction": sq.direction,
+ "traffic-queues": trafficQueues,
+ "device-id": f.deviceHandler.device.Id})
+
+ if sq.direction == tp_pb.Direction_DOWNSTREAM {
+ multicastTrafficQueues := f.techprofile[sq.intfID].GetMulticastTrafficQueues(ctx, sq.tpInst.(*tp.TechProfile))
+ 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
+ logger.Debugw(ctx, "multicast-traffic-queues", log.Fields{"device-id": f.deviceHandler.device.Id})
+ multicastQueuePerPonPort := multicastTrafficQueues[0]
+ f.interfaceToMcastQueueMap[sq.intfID] = &queueInfoBrief{
+ gemPortID: multicastQueuePerPonPort.GemportId,
+ servicePriority: multicastQueuePerPonPort.Priority,
+ }
+ //also store the queue info in kv store
+ if err := f.resourceMgr.AddMcastQueueForIntf(ctx, sq.intfID, multicastQueuePerPonPort.GemportId, multicastQueuePerPonPort.Priority); err != nil {
+ logger.Errorw(ctx, "failed-to-add-mcast-queue", log.Fields{"error": err})
+ return err
+ }
+
+ logger.Infow(ctx, "multicast-queues-successfully-updated", log.Fields{"device-id": f.deviceHandler.device.Id})
+ }
+ }
+ }
+ 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
+ logger.Infow(ctx, "removing-schedulers-and-queues-in-olt",
+ log.Fields{
+ "direction": sq.direction,
+ "intf-id": sq.intfID,
+ "onu-id": sq.onuID,
+ "uni-id": sq.uniID,
+ "uni-port": sq.uniPort,
+ "device-id": f.deviceHandler.device.Id})
+ if sq.direction == tp_pb.Direction_UPSTREAM {
+ SchedCfg, err = f.techprofile[sq.intfID].GetUsScheduler(ctx, sq.tpInst.(*tp.TechProfile))
+ Direction = "upstream"
+ } else if sq.direction == tp_pb.Direction_DOWNSTREAM {
+ SchedCfg, err = f.techprofile[sq.intfID].GetDsScheduler(ctx, sq.tpInst.(*tp.TechProfile))
+ Direction = "downstream"
+ }
+
+ if err != nil {
+ return olterrors.NewErrNotFound("scheduler-config",
+ log.Fields{
+ "int-id": sq.intfID,
+ "direction": sq.direction,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+
+ KVStoreMeter, err := f.resourceMgr.GetMeterIDForOnu(ctx, Direction, sq.intfID, sq.onuID, sq.uniID, sq.tpID)
+ if err != nil {
+ return olterrors.NewErrNotFound("meter",
+ log.Fields{
+ "onu-id": sq.onuID,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+ if KVStoreMeter == nil {
+ logger.Warnw(ctx, "no-meter-installed-yet",
+ log.Fields{
+ "direction": Direction,
+ "intf-id": sq.intfID,
+ "onu-id": sq.onuID,
+ "uni-id": sq.uniID,
+ "device-id": f.deviceHandler.device.Id})
+ 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.(*tp.TechProfile), SchedCfg, TrafficShaping)}
+ TrafficSched[0].TechProfileId = sq.tpID
+
+ TrafficQueues, err := f.techprofile[sq.intfID].GetTrafficQueues(ctx, sq.tpInst.(*tp.TechProfile), sq.direction)
+ if err != nil {
+ return olterrors.NewErrAdapter("unable-to-construct-traffic-queue-configuration",
+ log.Fields{
+ "intf-id": sq.intfID,
+ "direction": sq.direction,
+ "device-id": f.deviceHandler.device.Id}, 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,
+ TechProfileId: TrafficSched[0].TechProfileId}); err != nil {
+ return olterrors.NewErrAdapter("unable-to-remove-traffic-queues-from-device",
+ log.Fields{
+ "intf-id": sq.intfID,
+ "traffic-queues": TrafficQueues,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+ logger.Infow(ctx, "removed-traffic-queues-successfully", log.Fields{"device-id": f.deviceHandler.device.Id})
+ 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 {
+ return olterrors.NewErrAdapter("unable-to-remove-traffic-schedulers-from-device",
+ log.Fields{
+ "intf-id": sq.intfID,
+ "traffic-schedulers": TrafficSched}, err)
+ }
+
+ logger.Infow(ctx, "removed-traffic-schedulers-successfully", log.Fields{"device-id": f.deviceHandler.device.Id})
+
+ /* 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 {
+ return olterrors.NewErrAdapter("unable-to-remove-meter",
+ log.Fields{
+ "onu": sq.onuID,
+ "meter": KVStoreMeter.MeterId,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+ logger.Infow(ctx, "removed-meter-from-KV-store-successfully",
+ log.Fields{
+ "meter-id": KVStoreMeter.MeterId,
+ "dir": Direction,
+ "device-id": f.deviceHandler.device.Id})
+ 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, interface{}) {
+ 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(ctx, intfID, uni, TpID)
+
+ logger.Debugw(ctx, "creating-new-tcont-and-gem", log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "device-id": f.deviceHandler.device.Id,
+ "tp-id": TpID})
+
+ techProfileInstance, _ := f.techprofile[intfID].GetTPInstanceFromKVStore(ctx, TpID, tpPath)
+ if techProfileInstance == nil {
+ logger.Infow(ctx, "tp-instance-not-found--creating-new",
+ log.Fields{
+ "path": tpPath,
+ "device-id": f.deviceHandler.device.Id})
+ techProfileInstance, err = f.techprofile[intfID].CreateTechProfInstance(ctx, TpID, uni, intfID)
+ if err != nil {
+ // This should not happen, something wrong in KV backend transaction
+ logger.Errorw(ctx, "tp-instance-create-failed",
+ log.Fields{
+ "error": err,
+ "tp-id": TpID,
+ "device-id": f.deviceHandler.device.Id})
+ return 0, nil, nil
+ }
+ if err := f.resourceMgr.UpdateTechProfileIDForOnu(ctx, intfID, onuID, uniID, TpID); err != nil {
+ logger.Warnw(ctx, "failed-to-update-tech-profile-id", log.Fields{"error": err})
+ }
+ } else {
+ logger.Debugw(ctx, "tech-profile-instance-already-exist-for-given port-name",
+ log.Fields{
+ "uni": uni,
+ "device-id": f.deviceHandler.device.Id})
+ tpInstanceExists = true
+ }
+
+ switch tpInst := techProfileInstance.(type) {
+ case *tp.TechProfile:
+ 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 {
+ logger.Errorw(ctx, "CreateSchedulerQueues-failed-upstream",
+ log.Fields{
+ "error": err,
+ "meter-id": UsMeterID,
+ "device-id": f.deviceHandler.device.Id})
+ 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 {
+ logger.Errorw(ctx, "CreateSchedulerQueues-failed-downstream",
+ log.Fields{
+ "error": err,
+ "meter-id": DsMeterID,
+ "device-id": f.deviceHandler.device.Id})
+ return 0, nil, nil
+ }
+ }
+ allocID := tpInst.UsScheduler.AllocID
+ for _, gem := range tpInst.UpstreamGemPortAttributeList {
+ gemPortIDs = append(gemPortIDs, gem.GemportID)
+ }
+ allocIDs = appendUnique(allocIDs, allocID)
+
+ if tpInstanceExists {
+ return allocID, gemPortIDs, techProfileInstance
+ }
+
+ for _, gemPortID := range gemPortIDs {
+ allgemPortIDs = appendUnique(allgemPortIDs, gemPortID)
+ }
+ logger.Infow(ctx, "allocated-tcont-and-gem-ports",
+ log.Fields{
+ "alloc-ids": allocIDs,
+ "gemports": allgemPortIDs,
+ "device-id": f.deviceHandler.device.Id})
+ // Send Tconts and GEM ports to KV store
+ f.storeTcontsGEMPortsIntoKVStore(ctx, intfID, onuID, uniID, allocIDs, allgemPortIDs)
+ return allocID, gemPortIDs, techProfileInstance
+ case *tp.EponProfile:
+ // CreateSchedulerQueues for EPON needs to be implemented here
+ // when voltha-protos for EPON is completed.
+
+ // TODO
+ if UsMeterID != 0 {
+ go func() {
+ 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.L2oamCreateSchedulerQueues(ctx, sq); err != nil {
+ logger.Errorw(ctx, "CreateSchedulerQueues-failed-upstream",
+ log.Fields{
+ "error": err,
+ "meter-id": UsMeterID,
+ "device-id": f.deviceHandler.device.Id})
+ //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.L2oamCreateSchedulerQueues(ctx, sq); err != nil {
+ logger.Errorw(ctx, "CreateSchedulerQueues-failed-downstream",
+ log.Fields{
+ "error": err,
+ "meter-id": DsMeterID,
+ "device-id": f.deviceHandler.device.Id})
+ return 0, nil, nil
+ }
+ }
+ */
+
+ allocID := tpInst.AllocID
+ for _, gem := range tpInst.UpstreamQueueAttributeList {
+ gemPortIDs = append(gemPortIDs, gem.GemportID)
+ }
+ allocIDs = appendUnique(allocIDs, allocID)
+
+ if tpInstanceExists {
+ return allocID, gemPortIDs, techProfileInstance
+ }
+
+ for _, gemPortID := range gemPortIDs {
+ allgemPortIDs = appendUnique(allgemPortIDs, gemPortID)
+ }
+ logger.Infow(ctx, "allocated-tcont-and-gem-ports",
+ log.Fields{
+ "alloc-ids": allocIDs,
+ "gemports": allgemPortIDs,
+ "device-id": f.deviceHandler.device.Id})
+ // Send Tconts and GEM ports to KV store
+ f.storeTcontsGEMPortsIntoKVStore(ctx, intfID, onuID, uniID, allocIDs, allgemPortIDs)
+ return allocID, gemPortIDs, techProfileInstance
+ default:
+ logger.Errorw(ctx, "unknown-tech",
+ log.Fields{
+ "tpInst": tpInst})
+ return 0, nil, nil
+ }
+}
+
+func (f *OpenOltFlowMgr) storeTcontsGEMPortsIntoKVStore(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, allocID []uint32, gemPortIDs []uint32) {
+
+ logger.Debugw(ctx, "storing-allocated-tconts-and-gem-ports-into-KV-store",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "alloc-id": allocID,
+ "gemport-ids": gemPortIDs,
+ "device-id": f.deviceHandler.device.Id})
+ /* 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 {
+ logger.Errorw(ctx, "error-while-uploading-allocid-to-kv-store", log.Fields{"device-id": f.deviceHandler.device.Id})
+ }
+ if err := f.resourceMgr.UpdateGEMPortIDsForOnu(ctx, intfID, onuID, uniID, gemPortIDs); err != nil {
+ logger.Errorw(ctx, "error-while-uploading-gemports-to-kv-store", log.Fields{"device-id": f.deviceHandler.device.Id})
+ }
+ if err := f.resourceMgr.UpdateGEMportsPonportToOnuMapOnKVStore(ctx, gemPortIDs, intfID, onuID, uniID); err != nil {
+ logger.Error(ctx, "error-while-uploading-gemtopon-map-to-kv-store", log.Fields{"device-id": f.deviceHandler.device.Id})
+ }
+ logger.Infow(ctx, "stored-tconts-and-gem-into-kv-store-successfully", log.Fields{"device-id": f.deviceHandler.device.Id})
+ for _, gemPort := range gemPortIDs {
+ f.addGemPortToOnuInfoMap(ctx, intfID, onuID, gemPort)
+ }
+}
+
+func (f *OpenOltFlowMgr) populateTechProfilePerPonPort(ctx context.Context) 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++
+ logger.Debugw(ctx, "init-tech-profile-done",
+ log.Fields{
+ "intf-id": intfID,
+ "device-id": f.deviceHandler.device.Id})
+ }
+ }
+ if tpCount != int(f.resourceMgr.DevInfo.GetPonPorts()) {
+ return olterrors.NewErrInvalidValue(log.Fields{
+ "reason": "tP-count-does-not-match-number-of-pon-ports",
+ "tech-profile-count": tpCount,
+ "pon-port-count": f.resourceMgr.DevInfo.GetPonPorts(),
+ "device-id": f.deviceHandler.device.Id}, nil)
+ }
+ logger.Infow(ctx, "populated-techprofile-for-ponports-successfully",
+ log.Fields{
+ "numofTech": tpCount,
+ "numPonPorts": f.resourceMgr.DevInfo.GetPonPorts(),
+ "device-id": f.deviceHandler.device.Id})
+ 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, tpID uint32) error {
+ uplinkClassifier[PacketTagType] = SingleTag
+ logger.Debugw(ctx, "adding-upstream-data-flow",
+ log.Fields{
+ "uplinkClassifier": uplinkClassifier,
+ "uplinkAction": uplinkAction})
+ return f.addHSIAFlow(ctx, intfID, onuID, uniID, portNo, uplinkClassifier, uplinkAction,
+ Upstream, logicalFlow, allocID, gemportID, tpID)
+ /* 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, tpID uint32) error {
+ downlinkClassifier[PacketTagType] = DoubleTag
+ logger.Debugw(ctx, "adding-downstream-data-flow",
+ log.Fields{
+ "downlinkClassifier": downlinkClassifier,
+ "downlinkAction": downlinkAction})
+ 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(ctx, intfID, onuID, uniID) {
+ logger.Infow(ctx, "ignoring-dl-trap-device-flow-from-core",
+ log.Fields{
+ "flow": logicalFlow,
+ "device-id": f.deviceHandler.device.Id,
+ "onu-id": onuID,
+ "intf-id": intfID})
+ return nil
+ }
+ }
+ }
+ }
+
+ /* Already this info available classifier? */
+ downlinkAction[PopVlan] = true
+ dlClVid, ok := downlinkClassifier[VlanVid].(uint32)
+ if ok {
+ downlinkAction[VlanVid] = dlClVid & 0xfff
+ } else {
+ return olterrors.NewErrInvalidValue(log.Fields{
+ "reason": "failed-to-convert-vlanid-classifier",
+ "vlan-id": VlanVid,
+ "device-id": f.deviceHandler.device.Id}, nil).Log()
+ }
+
+ return f.addHSIAFlow(ctx, intfID, onuID, uniID, portNo, downlinkClassifier, downlinkAction,
+ Downstream, logicalFlow, allocID, gemportID, tpID)
+}
+
+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, tpID 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.
+ */
+ logger.Infow(ctx, "adding-hsia-flow",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "device-id": f.deviceHandler.device.Id,
+ "classifier": classifier,
+ "action": action,
+ "direction": direction,
+ "alloc-id": allocID,
+ "gemport-id": gemPortID,
+ "logicalflow": *logicalFlow})
+ var vlanPbit uint32 = 0xff // means no pbit
+ var vlanVid uint32
+ if _, ok := classifier[VlanPcp]; ok {
+ vlanPbit = classifier[VlanPcp].(uint32)
+ logger.Debugw(ctx, "found-pbit-in-flow",
+ log.Fields{
+ "vlan-pbit": vlanPbit,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "device-id": f.deviceHandler.device.Id})
+ } else {
+ logger.Debugw(ctx, "pbit-not-found-in-flow",
+ log.Fields{
+ "vlan-pcp": VlanPcp,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "device-id": f.deviceHandler.device.Id})
+ }
+ if _, ok := classifier[VlanVid]; ok {
+ vlanVid = classifier[VlanVid].(uint32)
+ logger.Debugw(ctx, "found-vlan-in-the-flow",
+ log.Fields{
+ "vlan-vid": vlanVid,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "device-id": f.deviceHandler.device.Id})
+ }
+ flowStoreCookie := getFlowStoreCookie(ctx, classifier, gemPortID)
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(intfID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ logger.Infow(ctx, "flow-already-exists",
+ log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID,
+ "onu-id": onuID})
+ return nil
+ }
+ flowID, err := f.resourceMgr.GetFlowID(ctx, intfID, int32(onuID), int32(uniID), gemPortID, flowStoreCookie, HsiaFlow, vlanVid, vlanPbit)
+ if err != nil {
+ return olterrors.NewErrNotFound("hsia-flow-id",
+ log.Fields{
+ "direction": direction,
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ }, err).Log()
+ }
+ classifierProto, err := makeOpenOltClassifierField(classifier)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"classifier": classifier, "device-id": f.deviceHandler.device.Id}, err).Log()
+ }
+ logger.Debugw(ctx, "created-classifier-proto",
+ log.Fields{
+ "classifier": *classifierProto,
+ "device-id": f.deviceHandler.device.Id})
+ actionProto, err := makeOpenOltActionField(action, classifier)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"action": action, "device-id": f.deviceHandler.device.Id}, err).Log()
+ }
+ logger.Debugw(ctx, "created-action-proto",
+ log.Fields{
+ "action": *actionProto,
+ "device-id": f.deviceHandler.device.Id})
+ networkIntfID, err := getNniIntfID(ctx, classifier, action)
+ if err != nil {
+ return olterrors.NewErrNotFound("nni-interface-id",
+ log.Fields{
+ "classifier": classifier,
+ "action": action,
+ "device-id": f.deviceHandler.device.Id,
+ }, 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,
+ TechProfileId: tpID,
+ }
+ if err := f.addFlowToDevice(ctx, logicalFlow, &flow); err != nil {
+ return olterrors.NewErrFlowOp("add", flowID, nil, err).Log()
+ }
+ logger.Infow(ctx, "hsia-flow-added-to-device-successfully",
+ log.Fields{"direction": direction,
+ "device-id": f.deviceHandler.device.Id,
+ "flow": flow,
+ "intf-id": intfID,
+ "onu-id": onuID})
+ 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 olterrors.NewErrPersistence("update", "flow", flowID,
+ log.Fields{
+ "flow": flow,
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID,
+ "onu-id": onuID}, err).Log()
+ }
+
+ go f.l2oamAddFlowDevice(ctx, intfID, onuID, classifierProto.OVid, classifierProto.IVid)
+
+ 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, tpID uint32) error {
+
+ networkIntfID, err := getNniIntfID(ctx, classifier, action)
+ if err != nil {
+ return olterrors.NewErrNotFound("nni-interface-id", log.Fields{
+ "classifier": classifier,
+ "action": action,
+ "device-id": f.deviceHandler.device.Id},
+ err).Log()
+ }
+
+ for k := range action {
+ delete(action, k)
+ }
+
+ action[TrapToHost] = true
+ classifier[UDPSrc] = uint32(68)
+ classifier[UDPDst] = uint32(67)
+ classifier[PacketTagType] = SingleTag
+
+ flowStoreCookie := getFlowStoreCookie(ctx, classifier, gemPortID)
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(intfID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ logger.Infow(ctx, "flow-exists--not-re-adding",
+ log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID,
+ "onu-id": onuID})
+ 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 olterrors.NewErrNotFound("flow",
+ log.Fields{
+ "interface-id": intfID,
+ "gem-port": gemPortID,
+ "cookie": flowStoreCookie,
+ "device-id": f.deviceHandler.device.Id},
+ err).Log()
+ }
+
+ logger.Debugw(ctx, "creating-ul-dhcp-flow",
+ log.Fields{
+ "ul_classifier": classifier,
+ "ul_action": action,
+ "uplinkFlowId": flowID,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "device-id": f.deviceHandler.device.Id})
+
+ classifierProto, err := makeOpenOltClassifierField(classifier)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"classifier": classifier}, err).Log()
+ }
+ logger.Debugw(ctx, "created-classifier-proto", log.Fields{"classifier": *classifierProto})
+ actionProto, err := makeOpenOltActionField(action, classifier)
+ if err != nil {
+ return olterrors.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,
+ TechProfileId: tpID,
+ }
+ if err := f.addFlowToDevice(ctx, logicalFlow, &dhcpFlow); err != nil {
+ return olterrors.NewErrFlowOp("add", flowID, log.Fields{"dhcp-flow": dhcpFlow}, err).Log()
+ }
+ logger.Infow(ctx, "dhcp-ul-flow-added-to-device-successfully",
+ log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "flow-id": flowID,
+ "intf-id": intfID,
+ "onu-id": onuID})
+ 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 olterrors.NewErrPersistence("update", "flow", dhcpFlow.FlowId,
+ log.Fields{
+ "flow": dhcpFlow,
+ "device-id": f.deviceHandler.device.Id}, 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, tpID uint32) error {
+ return f.addUpstreamTrapFlow(ctx, intfID, onuID, uniID, portNo, classifier, action, logicalFlow, allocID, gemPortID, IgmpFlow, tpID)
+}
+
+//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, tpID uint32) error {
+
+ networkIntfID, err := getNniIntfID(ctx, classifier, action)
+ if err != nil {
+ return olterrors.NewErrNotFound("nni-interface-id",
+ log.Fields{
+ "classifier": classifier,
+ "action": action,
+ "device-id": f.deviceHandler.device.Id},
+ err).Log()
+ }
+
+ for k := range action {
+ delete(action, k)
+ }
+
+ action[TrapToHost] = true
+ classifier[PacketTagType] = SingleTag
+ delete(classifier, VlanVid)
+
+ flowStoreCookie := getFlowStoreCookie(ctx, classifier, gemPortID)
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(networkIntfID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ logger.Infow(ctx, "flow-exists-not-re-adding", log.Fields{"device-id": f.deviceHandler.device.Id})
+ 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 olterrors.NewErrNotFound("flow-id",
+ log.Fields{
+ "intf-id": intfID,
+ "oni-id": onuID,
+ "cookie": flowStoreCookie,
+ "flow-type": flowType,
+ "device-id": f.deviceHandler.device.Id,
+ "onu-id": onuID},
+ err).Log()
+ }
+
+ logger.Debugw(ctx, "creating-upstream-trap-flow",
+ log.Fields{
+ "ul_classifier": classifier,
+ "ul_action": action,
+ "uplinkFlowId": flowID,
+ "flowType": flowType,
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID,
+ "onu-id": onuID})
+
+ classifierProto, err := makeOpenOltClassifierField(classifier)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"classifier": classifier, "device-id": f.deviceHandler.device.Id}, err).Log()
+ }
+ logger.Debugw(ctx, "created-classifier-proto",
+ log.Fields{
+ "classifier": *classifierProto,
+ "device-id": f.deviceHandler.device.Id})
+ actionProto, err := makeOpenOltActionField(action, classifier)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"action": action, "device-id": f.deviceHandler.device.Id}, 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,
+ TechProfileId: tpID,
+ }
+
+ if err := f.addFlowToDevice(ctx, logicalFlow, &flow); err != nil {
+ return olterrors.NewErrFlowOp("add", flowID, log.Fields{"flow": flow, "device-id": f.deviceHandler.device.Id}, err).Log()
+ }
+ logger.Infof(ctx, "%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 olterrors.NewErrPersistence("update", "flow", flow.FlowId, log.Fields{"flow": flow, "device-id": f.deviceHandler.device.Id}, 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, tpID uint32) error {
+ logger.Infow(ctx, "adding-eapol-to-device",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "port-no": portNo,
+ "alloc-id": allocID,
+ "gemport-id": gemPortID,
+ "vlan-id": vlanID,
+ "flow": logicalFlow})
+
+ uplinkClassifier := make(map[string]interface{})
+ uplinkAction := make(map[string]interface{})
+
+ uplinkClassifier[EthType] = uint32(EapEthType)
+ uplinkClassifier[PacketTagType] = SingleTag
+ uplinkClassifier[VlanVid] = vlanID
+ uplinkClassifier[VlanPcp] = classifier[VlanPcp]
+ uplinkAction[TrapToHost] = true
+ flowStoreCookie := getFlowStoreCookie(ctx, uplinkClassifier, gemPortID)
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(intfID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ logger.Infow(ctx, "flow-exists-not-re-adding", log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "onu-id": onuID,
+ "intf-id": intfID})
+ return nil
+ }
+ uplinkFlowID, err := f.resourceMgr.GetFlowID(ctx, intfID, int32(onuID), int32(uniID), gemPortID, flowStoreCookie, "", 0, 0)
+ if err != nil {
+ return olterrors.NewErrNotFound("flow-id",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "coookie": flowStoreCookie,
+ "device-id": f.deviceHandler.device.Id},
+ err).Log()
+ }
+ logger.Debugw(ctx, "creating-ul-eapol-flow",
+ log.Fields{
+ "ul_classifier": uplinkClassifier,
+ "ul_action": uplinkAction,
+ "uplinkFlowId": uplinkFlowID,
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID,
+ "onu-id": onuID})
+
+ classifierProto, err := makeOpenOltClassifierField(uplinkClassifier)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{
+ "classifier": uplinkClassifier,
+ "device-id": f.deviceHandler.device.Id}, err).Log()
+ }
+ logger.Debugw(ctx, "created-classifier-proto",
+ log.Fields{
+ "classifier": *classifierProto,
+ "device-id": f.deviceHandler.device.Id})
+ actionProto, err := makeOpenOltActionField(uplinkAction, uplinkClassifier)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"action": uplinkAction, "device-id": f.deviceHandler.device.Id}, err).Log()
+ }
+ logger.Debugw(ctx, "created-action-proto",
+ log.Fields{
+ "action": *actionProto,
+ "device-id": f.deviceHandler.device.Id})
+ networkIntfID, err := getNniIntfID(ctx, classifier, action)
+ if err != nil {
+ return olterrors.NewErrNotFound("nni-interface-id", log.Fields{
+ "classifier": classifier,
+ "action": action,
+ "device-id": f.deviceHandler.device.Id},
+ 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,
+ TechProfileId: tpID,
+ }
+ if err := f.addFlowToDevice(ctx, logicalFlow, &upstreamFlow); err != nil {
+ return olterrors.NewErrFlowOp("add", uplinkFlowID, log.Fields{"flow": upstreamFlow}, err).Log()
+ }
+ logger.Infow(ctx, "eapol-ul-flow-added-to-device-successfully",
+ log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "onu-id": onuID,
+ "intf-id": intfID,
+ })
+ 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 olterrors.NewErrPersistence("update", "flow", upstreamFlow.FlowId,
+ log.Fields{
+ "flow": upstreamFlow,
+ "device-id": f.deviceHandler.device.Id}, err).Log()
+ }
+ 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 {
+ if vlanID != ReservedVlan {
+ vid := vlanID & VlanvIDMask
+ classifier.OVid = vid
+ }
+ }
+ if metadata, ok := classifierInfo[Metadata].(uint64); ok {
+ vid := uint32(metadata)
+ if vid != ReservedVlan {
+ classifier.IVid = vid
+ }
+ }
+ 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, olterrors.NewErrInvalidValue(log.Fields{"packet-tag-type": pktTagType}, nil)
+ }
+ }
+ return &classifier, nil
+}
+
+func makeOpenOltActionField(actionInfo map[string]interface{}, classifierInfo map[string]interface{}) (*openoltpb2.Action, error) {
+ var actionCmd openoltpb2.ActionCmd
+ var action openoltpb2.Action
+ action.Cmd = &actionCmd
+ if _, ok := actionInfo[PopVlan]; ok {
+ action.Cmd.RemoveOuterTag = true
+ if _, ok := actionInfo[VlanPcp]; ok {
+ action.Cmd.RemarkInnerPbits = true
+ action.IPbits = actionInfo[VlanPcp].(uint32)
+ if _, ok := actionInfo[VlanVid]; ok {
+ action.Cmd.TranslateInnerTag = true
+ action.IVid = actionInfo[VlanVid].(uint32)
+ }
+ }
+ } else if _, ok := actionInfo[PushVlan]; ok {
+ action.OVid = actionInfo[VlanVid].(uint32)
+ action.Cmd.AddOuterTag = true
+ if _, ok := actionInfo[VlanPcp]; ok {
+ action.OPbits = actionInfo[VlanPcp].(uint32)
+ action.Cmd.RemarkOuterPbits = true
+ if _, ok := classifierInfo[VlanVid]; ok {
+ action.IVid = classifierInfo[VlanVid].(uint32)
+ action.Cmd.TranslateInnerTag = true
+ }
+ }
+ } else if _, ok := actionInfo[TrapToHost]; ok {
+ action.Cmd.TrapToHost = actionInfo[TrapToHost].(bool)
+ } else {
+ return nil, olterrors.NewErrInvalidValue(log.Fields{"action-command": actionInfo}, nil)
+ }
+ return &action, nil
+}
+
+// getTPpath return the ETCD path for a given UNI port
+func (f *OpenOltFlowMgr) getTPpath(ctx context.Context, intfID uint32, uniPath string, TpID uint32) string {
+ return f.techprofile[intfID].GetTechProfileInstanceKVPath(ctx, TpID, uniPath)
+}
+
+// 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 := getUniPortPath(f.deviceHandler.device.Id, intfID, int32(onuID), int32(uniID))
+
+ for _, tpID := range tpIDList {
+ if err := f.DeleteTechProfileInstance(ctx, intfID, onuID, uniID, uniPortName, tpID); err != nil {
+ _ = olterrors.NewErrAdapter("delete-tech-profile-failed", log.Fields{"device-id": f.deviceHandler.device.Id}, err).Log()
+ // return err
+ // We should continue to delete tech-profile instances for other TP IDs
+ }
+ logger.Debugw(ctx, "tech-profile-deleted", log.Fields{"device-id": f.deviceHandler.device.Id, "tp-id": tpID})
+ }
+ 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 = getUniPortPath(f.deviceHandler.device.Id, intfID, int32(onuID), int32(uniID))
+ }
+ if err := f.techprofile[intfID].DeleteTechProfileInstance(ctx, tpID, uniPortName); err != nil {
+ return olterrors.NewErrAdapter("failed-to-delete-tp-instance-from-kv-store",
+ log.Fields{
+ "tp-id": tpID,
+ "uni-port-name": uniPortName,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+ return nil
+}
+
+func getFlowStoreCookie(ctx context.Context, classifier map[string]interface{}, gemPortID uint32) uint64 {
+ if len(classifier) == 0 { // should never happen
+ logger.Error(ctx, "invalid-classfier-object")
+ return 0
+ }
+ logger.Debugw(ctx, "generating-flow-store-cookie",
+ log.Fields{
+ "classifier": classifier,
+ "gemport-id": gemPortID})
+ var jsonData []byte
+ var flowString string
+ var err error
+ if jsonData, err = json.Marshal(classifier); err != nil {
+ logger.Error(ctx, "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()
+ logger.Debugw(ctx, "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)
+ }
+ existingFlows := f.resourceMgr.GetFlowIDInfo(ctx, intfID, flow.OnuId, flow.UniId, flow.FlowId)
+ if existingFlows != nil {
+ logger.Debugw(ctx, "flow-exists-for-given-flowID--appending-it-to-current-flow",
+ log.Fields{
+ "flow-id": flow.FlowId,
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID,
+ "onu-id": flow.OnuId})
+ //for _, f := range *existingFlows {
+ // flows = append(flows, f)
+ //}
+ flows = append(flows, *existingFlows...)
+ }
+ logger.Debugw(ctx, "updated-flows-for-given-flowID-and-onuid",
+ log.Fields{
+ "updatedflow": flows,
+ "flow-id": flow.FlowId,
+ "onu-id": flow.OnuId,
+ "device-id": f.deviceHandler.device.Id})
+ return &flows
+}
+
+func (f *OpenOltFlowMgr) updateFlowInfoToKVStore(ctx context.Context, intfID int32, onuID int32, uniID int32, flowID uint32, flows *[]rsrcMgr.FlowInfo) error {
+ logger.Debugw(ctx, "storing-flow(s)-into-kv-store", log.Fields{
+ "flow-id": flowID,
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID,
+ "onu-id": onuID})
+ if err := f.resourceMgr.UpdateFlowIDInfo(ctx, intfID, onuID, uniID, flowID, flows); err != nil {
+ logger.Warnw(ctx, "error-while-storing-flow-into-kv-store", log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "onu-id": onuID,
+ "intf-id": intfID,
+ "flow-id": flowID})
+ return err
+ }
+ logger.Infow(ctx, "stored-flow(s)-into-kv-store-successfully!", log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "onu-id": onuID,
+ "intf-id": intfID,
+ "flow-id": flowID})
+ 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)
+ }
+
+ logger.Debugw(ctx, "sending-flow-to-device-via-grpc", log.Fields{
+ "flow": *deviceFlow,
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID})
+ _, err := f.deviceHandler.Client.FlowAdd(log.WithSpanFromContext(context.Background(), ctx), deviceFlow)
+
+ st, _ := status.FromError(err)
+ if st.Code() == codes.AlreadyExists {
+ logger.Debug(ctx, "flow-already-exists", log.Fields{
+ "err": err,
+ "deviceFlow": deviceFlow,
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID})
+ return nil
+ }
+
+ if err != nil {
+ logger.Errorw(ctx, "failed-to-add-flow-to-device",
+ log.Fields{"err": err,
+ "device-flow": deviceFlow,
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID})
+ 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.
+ if err := f.registerFlow(ctx, logicalFlow, deviceFlow); err != nil {
+ logger.Errorw(ctx, "failed-to-register-flow", log.Fields{"err": err})
+ return err
+ }
+ }
+ logger.Infow(ctx, "flow-added-to-device-successfully ",
+ log.Fields{
+ "flow": *deviceFlow,
+ "device-id": f.deviceHandler.device.Id,
+ "intf-id": intfID})
+ return nil
+}
+
+func (f *OpenOltFlowMgr) removeFlowFromDevice(ctx context.Context, deviceFlow *openoltpb2.Flow, ofFlowID uint64) error {
+ logger.Debugw(ctx, "sending-flow-to-device-via-grpc",
+ log.Fields{
+ "flow": *deviceFlow,
+ "device-id": f.deviceHandler.device.Id})
+ _, err := f.deviceHandler.Client.FlowRemove(log.WithSpanFromContext(context.Background(), ctx), deviceFlow)
+ if err != nil {
+ if f.deviceHandler.device.ConnectStatus == common.ConnectStatus_UNREACHABLE {
+ logger.Warnw(ctx, "can-not-remove-flow-from-device--unreachable",
+ log.Fields{
+ "err": err,
+ "deviceFlow": deviceFlow,
+ "device-id": f.deviceHandler.device.Id})
+ //Assume the flow is removed
+ return nil
+ }
+ return olterrors.NewErrFlowOp("remove", deviceFlow.FlowId, log.Fields{"deviceFlow": deviceFlow}, err)
+
+ }
+ logger.Infow(ctx, "flow-removed-from-device-successfully", log.Fields{
+ "of-flow-id": ofFlowID,
+ "flow": *deviceFlow,
+ "device-id": f.deviceHandler.device.Id,
+ })
+ return nil
+}
+
+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
+
+
+ var onuID = -1
+ var uniID = -1
+ var gemPortID = -1
+
+ networkInterfaceID, err := IntfIDFromNniPortNum(ctx, portNo)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"nni-port-number": portNo}, err).Log()
+ }
+ var flowStoreCookie = getFlowStoreCookie(ctx, classifierInfo, uint32(0))
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ logger.Infow(ctx, "flow-exists--not-re-adding", log.Fields{"device-id": f.deviceHandler.device.Id})
+ return nil
+ }
+ flowID, err := f.resourceMgr.GetFlowID(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), uint32(gemPortID), flowStoreCookie, "", 0)
+
+ if err != nil {
+ return olterrors.NewErrNotFound("flow-id",
+ log.Fields{
+ "interface-id": networkInterfaceID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "gem-port-id": gemPortID,
+ "cookie": flowStoreCookie,
+ "device-id": f.deviceHandler.device.Id},
+ err)
+ }
+ classifierProto, err := makeOpenOltClassifierField(classifierInfo)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(
+ log.Fields{
+ "classifier": classifierInfo,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+ logger.Debugw(ctx, "created-classifier-proto",
+ log.Fields{
+ "classifier": *classifierProto,
+ "device-id": f.deviceHandler.device.Id})
+ actionProto, err := makeOpenOltActionField(actionInfo, classifierInfo)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(
+ log.Fields{
+ "action": actionInfo,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+ logger.Debugw(ctx, "created-action-proto",
+ log.Fields{
+ "action": *actionProto,
+ "device-id": f.deviceHandler.device.Id})
+
+ 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 olterrors.NewErrFlowOp("add", flowID,
+ log.Fields{
+ "flow": downstreamflow,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+ logger.Infow(ctx, "lldp-trap-on-nni-flow-added-to-device-successfully",
+ log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "onu-id": onuID,
+ "flow-id": flowID})
+ 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 olterrors.NewErrPersistence("update", "flow", flowID,
+ log.Fields{
+ "flow": downstreamflow,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+ return nil
+}
+
+func getUniPortPath(oltID string, intfID uint32, onuID int32, uniID int32) string {
+ return fmt.Sprintf("olt-{%s}/pon-{%d}/onu-{%d}/uni-{%d}", oltID, intfID, onuID, uniID)
+}
+
+//getOnuDevice to fetch onu from cache or core.
+func (f *OpenOltFlowMgr) getOnuDevice(ctx context.Context, intfID uint32, onuID uint32) (*OnuDevice, error) {
+ onuKey := f.deviceHandler.formOnuKey(intfID, onuID)
+ onuDev, ok := f.deviceHandler.onus.Load(onuKey)
+ if !ok {
+ logger.Debugw(ctx, "couldnt-find-onu-in-cache",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "device-id": f.deviceHandler.device.Id})
+ onuDevice, err := f.getChildDevice(ctx, intfID, onuID)
+ if err != nil {
+ return nil, olterrors.NewErrNotFound("onu-child-device",
+ log.Fields{
+ "onu-id": onuID,
+ "intf-id": intfID,
+ "device-id": f.deviceHandler.device.Id}, err)
+ }
+ onuDev = NewOnuDevice(onuDevice.Id, onuDevice.Type, onuDevice.SerialNumber, onuDevice.ProxyAddress.OnuId, onuDevice.ProxyAddress.ChannelId, onuDevice.ProxyAddress.DeviceId, false)
+ //better to ad the device to cache here.
+ f.deviceHandler.StoreOnuDevice(onuDev.(*OnuDevice))
+ } else {
+ logger.Debugw(ctx, "found-onu-in-cache",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "device-id": f.deviceHandler.device.Id})
+ }
+
+ return onuDev.(*OnuDevice), nil
+}
+
+//getChildDevice to fetch onu
+func (f *OpenOltFlowMgr) getChildDevice(ctx context.Context, intfID uint32, onuID uint32) (*voltha.Device, error) {
+ logger.Infow(ctx, "GetChildDevice",
+ log.Fields{
+ "pon-port": intfID,
+ "onu-id": onuID,
+ "device-id": f.deviceHandler.device.Id})
+ parentPortNo := IntfIDToPortNo(intfID, voltha.Port_PON_OLT)
+ onuDevice, err := f.deviceHandler.GetChildDevice(ctx, parentPortNo, onuID)
+ if err != nil {
+ return nil, olterrors.NewErrNotFound("onu",
+ log.Fields{
+ "interface-id": parentPortNo,
+ "onu-id": onuID,
+ "device-id": f.deviceHandler.device.Id},
+ err)
+ }
+ logger.Infow(ctx, "successfully-received-child-device-from-core",
+ log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "child_device_id": onuDevice.Id,
+ "child_device_sn": onuDevice.SerialNumber})
+ return onuDevice, nil
+}
+
+func (f *OpenOltFlowMgr) sendDeleteGemPortToChild(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, gemPortID uint32, tpPath string) error {
+ onuDev, err := f.getOnuDevice(ctx, intfID, onuID)
+ if err != nil {
+ logger.Debugw(ctx, "couldnt-find-onu-child-device",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "device-id": f.deviceHandler.device.Id})
+ return err
+ }
+
+ delGemPortMsg := &ic.InterAdapterDeleteGemPortMessage{UniId: uniID, TpPath: tpPath, GemPortId: gemPortID}
+ logger.Debugw(ctx, "sending-gem-port-delete-to-openonu-adapter",
+ log.Fields{
+ "msg": *delGemPortMsg,
+ "device-id": f.deviceHandler.device.Id})
+ if sendErr := f.deviceHandler.AdapterProxy.SendInterAdapterMessage(log.WithSpanFromContext(context.Background(), ctx),
+ delGemPortMsg,
+ ic.InterAdapterMessageType_DELETE_GEM_PORT_REQUEST,
+ f.deviceHandler.device.Type,
+ onuDev.deviceType,
+ onuDev.deviceID,
+ onuDev.proxyDeviceID, ""); sendErr != nil {
+ return olterrors.NewErrCommunication("send-delete-gem-port-to-onu-adapter",
+ log.Fields{
+ "from-adapter": f.deviceHandler.device.Type,
+ "to-adapter": onuDev.deviceType,
+ "onu-id": onuDev.deviceID,
+ "proxyDeviceID": onuDev.proxyDeviceID,
+ "device-id": f.deviceHandler.device.Id}, sendErr)
+ }
+ logger.Infow(ctx, "success-sending-del-gem-port-to-onu-adapter",
+ log.Fields{
+ "msg": delGemPortMsg,
+ "from-adapter": f.deviceHandler.device.Type,
+ "to-adapter": onuDev.deviceType,
+ "device-id": f.deviceHandler.device.Id})
+ return nil
+}
+
+func (f *OpenOltFlowMgr) sendDeleteTcontToChild(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, allocID uint32, tpPath string) error {
+ onuDev, err := f.getOnuDevice(ctx, intfID, onuID)
+ if err != nil {
+ logger.Warnw(ctx, "couldnt-find-onu-child-device",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "device-id": f.deviceHandler.device.Id})
+ return err
+ }
+
+ delTcontMsg := &ic.InterAdapterDeleteTcontMessage{UniId: uniID, TpPath: tpPath, AllocId: allocID}
+ logger.Debugw(ctx, "sending-tcont-delete-to-openonu-adapter",
+ log.Fields{
+ "msg": *delTcontMsg,
+ "device-id": f.deviceHandler.device.Id})
+ if sendErr := f.deviceHandler.AdapterProxy.SendInterAdapterMessage(log.WithSpanFromContext(context.Background(), ctx),
+ delTcontMsg,
+ ic.InterAdapterMessageType_DELETE_TCONT_REQUEST,
+ f.deviceHandler.device.Type,
+ onuDev.deviceType,
+ onuDev.deviceID,
+ onuDev.proxyDeviceID, ""); sendErr != nil {
+ return olterrors.NewErrCommunication("send-delete-tcont-to-onu-adapter",
+ log.Fields{
+ "from-adapter": f.deviceHandler.device.Type,
+ "to-adapter": onuDev.deviceType, "onu-id": onuDev.deviceID,
+ "proxyDeviceID": onuDev.proxyDeviceID,
+ "device-id": f.deviceHandler.device.Id}, sendErr)
+ }
+ logger.Infow(ctx, "success-sending-del-tcont-to-onu-adapter",
+ log.Fields{
+ "msg": delTcontMsg,
+ "device-id": f.deviceHandler.device.Id})
+ return nil
+}
+
+func (f *OpenOltFlowMgr) deletePendingFlows(ctx context.Context, 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 {
+ logger.Debugw(ctx, "flow-delete-succeeded--more-pending",
+ log.Fields{
+ "intf": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "currpendingflowcnt": pnFlDels,
+ "device-id": f.deviceHandler.device.Id})
+ f.pendingFlowDelete.Store(pnFlDelKey, pnFlDels)
+ } else {
+ logger.Debugw(ctx, "all-pending-flow-deletes-handled--removing-entry-from-map",
+ log.Fields{
+ "intf": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "device-id": f.deviceHandler.device.Id})
+ f.pendingFlowDelete.Delete(pnFlDelKey)
+ }
+ }
+ } else {
+ logger.Debugw(ctx, "no-pending-delete-flows-found",
+ log.Fields{
+ "intf": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "device-id": f.deviceHandler.device.Id})
+
+ }
+
+}
+
+// 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(ctx context.Context, intfID uint32, onuID uint32, gemPortID uint32) {
+
+ f.onuGemInfoLock.Lock()
+ defer f.onuGemInfoLock.Unlock()
+
+ logger.Infow(ctx, "deleting-gem-from-local-cache",
+ log.Fields{
+ "gem-port-id": gemPortID,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "device-id": f.deviceHandler.device.Id,
+ "onu-gem": f.onuGemInfo[intfID]})
+ onugem := f.onuGemInfo[intfID]
+deleteLoop:
+ 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
+ logger.Infow(ctx, "removed-gemport-from-local-cache",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "deletedgemport-id": gemPortID,
+ "gemports": onu.GemPorts,
+ "device-id": f.deviceHandler.device.Id})
+ break deleteLoop
+ }
+ }
+ break deleteLoop
+ }
+ }
+}
+
+//clearResources clears pon resources in kv store and the device
+// nolint: gocyclo
+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(ctx, flow)
+ if err != nil {
+ return olterrors.NewErrNotFound("tp-id",
+ log.Fields{
+ "flow": flow,
+ "intf": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "device-id": f.deviceHandler.device.Id}, 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.
+ if err := f.updateFlowInfoToKVStore(ctx, int32(Intf), int32(onuID), int32(uniID), flowID, &updatedFlows); err != nil {
+ _ = olterrors.NewErrPersistence("update", "flow", flowID,
+ log.Fields{
+ "flow": updatedFlows,
+ "device-id": f.deviceHandler.device.Id}, err).Log()
+ }
+ 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 {
+ logger.Debugw(ctx, "creating-entry-for-pending-flow-delete",
+ log.Fields{
+ "flow-id": flowID,
+ "intf": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "device-id": f.deviceHandler.device.Id})
+ f.pendingFlowDelete.Store(pnFlDelKey, 1)
+ } else {
+ pnFlDels := val.(int) + 1
+ logger.Debugw(ctx, "updating-flow-delete-entry",
+ log.Fields{
+ "flow-id": flowID,
+ "intf": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "currPendingFlowCnt": pnFlDels,
+ "device-id": f.deviceHandler.device.Id})
+ f.pendingFlowDelete.Store(pnFlDelKey, pnFlDels)
+ }
+
+ defer f.deletePendingFlows(ctx, Intf, onuID, uniID)
+ }
+
+ logger.Debugw(ctx, "releasing-flow-id-to-resource-manager",
+ log.Fields{
+ "Intf": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "flow-id": flowID,
+ "device-id": f.deviceHandler.device.Id})
+ f.resourceMgr.FreeFlowID(ctx, Intf, int32(onuID), int32(uniID), flowID)
+
+ uni := getUniPortPath(f.deviceHandler.device.Id, Intf, onuID, uniID)
+ tpPath := f.getTPpath(ctx, Intf, uni, tpID)
+ logger.Debugw(ctx, "getting-techprofile-instance-for-subscriber",
+ log.Fields{
+ "TP-PATH": tpPath,
+ "device-id": f.deviceHandler.device.Id})
+ techprofileInst, err := f.techprofile[Intf].GetTPInstanceFromKVStore(ctx, tpID, tpPath)
+ if err != nil || techprofileInst == nil { // This should not happen, something wrong in KV backend transaction
+ return olterrors.NewErrNotFound("tech-profile-in-kv-store",
+ log.Fields{
+ "tp-id": tpID,
+ "path": tpPath}, err)
+ }
+
+ gemPK := gemPortKey{Intf, uint32(gemPortID)}
+ used, err := f.isGemPortUsedByAnotherFlow(ctx, gemPK)
+ if err != nil {
+ return err
+ }
+ if used {
+ if f.perGemPortLock.TryLock(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
+ if err := f.resourceMgr.UpdateFlowIDsForGem(ctx, Intf, uint32(gemPortID), flowIDs); err != nil {
+ return err
+ }
+ break
+ }
+ }
+ logger.Debugw(ctx, "gem-port-id-is-still-used-by-other-flows",
+ log.Fields{
+ "gemport-id": gemPortID,
+ "usedByFlows": flowIDs,
+ "device-id": f.deviceHandler.device.Id})
+ f.perGemPortLock.Unlock(gemPK)
+ return nil
+ }
+
+ logger.Error(ctx, "failed-to-acquire-per-gem-port-lock",
+ log.Fields{
+ "gemport-id": gemPortID,
+ "device-id": f.deviceHandler.device.Id,
+ "key": gemPK,
+ })
+ return olterrors.NewErrAdapter("failed-to-acquire-per-gem-port-lock", log.Fields{
+ "gemport-id": gemPortID,
+ "device-id": f.deviceHandler.device.Id,
+ "key": gemPK,
+ }, nil)
+ }
+ logger.Debugf(ctx, "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(ctx, 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
+ if f.perGemPortLock.TryLock(gemPK) {
+ delete(f.flowsUsedByGemPort, gemPK)
+ f.perGemPortLock.Unlock(gemPK)
+ } else {
+ logger.Error(ctx, "failed-to-acquire-per-gem-port-lock",
+ log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "key": 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(ctx, Intf, uint32(onuID), uint32(uniID), uint32(gemPortID), tpPath); err != nil {
+ logger.Errorw(ctx, "error-processing-delete-gem-port-towards-onu",
+ log.Fields{
+ "err": err,
+ "intf": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "device-id": f.deviceHandler.device.Id,
+ "gemport-id": gemPortID})
+ }
+ switch techprofileInst := techprofileInst.(type) {
+ case *tp.TechProfile:
+ ok, _ := f.isTechProfileUsedByAnotherGem(ctx, Intf, uint32(onuID), uint32(uniID), tpID, techprofileInst, uint32(gemPortID))
+ if !ok {
+ if err := f.resourceMgr.RemoveTechProfileIDForOnu(ctx, Intf, uint32(onuID), uint32(uniID), tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ if err := f.DeleteTechProfileInstance(ctx, Intf, uint32(onuID), uint32(uniID), "", tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ if err := f.RemoveSchedulerQueues(ctx, schedQueue{direction: tp_pb.Direction_UPSTREAM, intfID: Intf, onuID: uint32(onuID), uniID: uint32(uniID), tpID: tpID, uniPort: portNum, tpInst: techprofileInst}); err != nil {
+ logger.Warn(ctx, err)
+ }
+ if err := f.RemoveSchedulerQueues(ctx, schedQueue{direction: tp_pb.Direction_DOWNSTREAM, intfID: Intf, onuID: uint32(onuID), uniID: uint32(uniID), tpID: tpID, uniPort: portNum, tpInst: techprofileInst}); err != nil {
+ logger.Warn(ctx, err)
+ }
+ f.resourceMgr.FreeAllocID(ctx, Intf, uint32(onuID), uint32(uniID), techprofileInst.UsScheduler.AllocID)
+ // Delete the TCONT on the ONU.
+ if err := f.sendDeleteTcontToChild(ctx, Intf, uint32(onuID), uint32(uniID), uint32(techprofileInst.UsScheduler.AllocID), tpPath); err != nil {
+ logger.Errorw(ctx, "error-processing-delete-tcont-towards-onu",
+ log.Fields{
+ "intf": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "device-id": f.deviceHandler.device.Id,
+ "alloc-id": techprofileInst.UsScheduler.AllocID})
+ }
+ }
+ case *tp.EponProfile:
+ if err := f.resourceMgr.RemoveTechProfileIDForOnu(ctx, Intf, uint32(onuID), uint32(uniID), tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ if err := f.DeleteTechProfileInstance(ctx, Intf, uint32(onuID), uint32(uniID), "", tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ f.resourceMgr.FreeAllocID(ctx, Intf, uint32(onuID), uint32(uniID), techprofileInst.AllocID)
+ // Delete the TCONT on the ONU.
+ if err := f.sendDeleteTcontToChild(ctx, Intf, uint32(onuID), uint32(uniID), uint32(techprofileInst.AllocID), tpPath); err != nil {
+ logger.Errorw(ctx, "error-processing-delete-tcont-towards-onu",
+ log.Fields{
+ "intf": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "device-id": f.deviceHandler.device.Id,
+ "alloc-id": techprofileInst.AllocID})
+ }
+ default:
+ logger.Errorw(ctx, "error-unknown-tech",
+ log.Fields{
+ "techprofileInst": techprofileInst})
+ }
+ }
+ }
+ return nil
+}
+
+// nolint: gocyclo
+func (f *OpenOltFlowMgr) clearFlowFromResourceManager(ctx context.Context, flow *ofp.OfpFlowStats, flowDirection string) {
+
+ logger.Infow(ctx, "clear-flow-from-resource-manager",
+ log.Fields{
+ "flowDirection": flowDirection,
+ "flow": *flow,
+ "device-id": f.deviceHandler.device.Id})
+
+ if flowDirection == Multicast {
+ f.clearMulticastFlowFromResourceManager(ctx, flow)
+ return
+ }
+
+ classifierInfo := make(map[string]interface{})
+
+ portNum, Intf, onu, uni, inPort, ethType, err := FlowExtractInfo(ctx, flow, flowDirection)
+ if err != nil {
+ logger.Error(ctx, err)
+ return
+ }
+
+ onuID := int32(onu)
+ uniID := int32(uni)
+
+ for _, field := range flows.GetOfbFields(flow) {
+ if field.Type == flows.IP_PROTO {
+ classifierInfo[IPProto] = field.GetIpProto()
+ logger.Debugw(ctx, "field-type-ip-proto", log.Fields{"classifierInfo[IP_PROTO]": classifierInfo[IPProto].(uint32)})
+ }
+ }
+ logger.Infow(ctx, "extracted-access-info-from-flow-to-be-deleted",
+ log.Fields{
+ "flow-id": flow.Id,
+ "intf-id": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID})
+
+ if ethType == LldpEthType || ((classifierInfo[IPProto] == IPProtoDhcp) && (flowDirection == "downstream")) {
+ onuID = -1
+ uniID = -1
+ logger.Debug(ctx, "trap-on-nni-flow-set-oni--uni-to- -1")
+ Intf, err = IntfIDFromNniPortNum(ctx, inPort)
+ if err != nil {
+ logger.Errorw(ctx, "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 {
+ logger.Debugw(ctx, "no-flowinfo-found-in-kv-store",
+ log.Fields{
+ "intf": Intf,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "flow-id": flowID})
+ return
+ }
+
+ updatedFlows := *flowInfo
+ for i, storedFlow := range updatedFlows {
+ if flow.Id == storedFlow.LogicalFlowID {
+ removeFlowMessage := openoltpb2.Flow{FlowId: storedFlow.Flow.FlowId, FlowType: storedFlow.Flow.FlowType}
+ logger.Debugw(ctx, "flow-to-be-deleted", log.Fields{"flow": storedFlow})
+ // DKB
+ if err = f.removeFlowFromDevice(ctx, &removeFlowMessage, flow.Id); err != nil {
+ logger.Errorw(ctx, "failed-to-remove-flow", log.Fields{"error": err})
+ return
+ }
+ logger.Info(ctx, "flow-removed-from-device-successfully", log.Fields{
+ "flow-id": flow.Id,
+ "stored-flow": storedFlow,
+ "device-id": f.deviceHandler.device.Id,
+ "stored-flow-id": flowID,
+ "onu-id": onuID,
+ "intf": Intf,
+ })
+ //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 {
+ logger.Error(ctx, "failed-to-clear-resources-for-flow", log.Fields{
+ "flow-id": flow.Id,
+ "stored-flow": storedFlow,
+ "device-id": f.deviceHandler.device.Id,
+ "stored-flow-id": flowID,
+ "onu-id": onuID,
+ "intf": Intf,
+ })
+ 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(ctx, classifierInfo, flow)
+ networkInterfaceID, err := f.getNNIInterfaceIDOfMulticastFlow(ctx, classifierInfo)
+
+ if err != nil {
+ logger.Warnw(ctx, "no-inport-found--cannot-release-resources-of-the-multicast-flow", log.Fields{"flowId:": flow.Id})
+ return
+ }
+
+ var onuID = int32(NoneOnuID)
+ var uniID = int32(NoneUniID)
+ var flowID uint32
+
+ flowIds := f.resourceMgr.GetCurrentFlowIDsForOnu(ctx, networkInterfaceID, onuID, uniID)
+
+ for _, flowID = range flowIds {
+ flowInfo := f.resourceMgr.GetFlowIDInfo(ctx, networkInterfaceID, onuID, uniID, flowID)
+ if flowInfo == nil {
+ logger.Debugw(ctx, "no-multicast-flowinfo-found-in-the-kv-store",
+ log.Fields{
+ "intf": networkInterfaceID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "flow-id": flowID})
+ continue
+ }
+ updatedFlows := *flowInfo
+ for i, storedFlow := range updatedFlows {
+ if flow.Id == storedFlow.LogicalFlowID {
+ removeFlowMessage := openoltpb2.Flow{FlowId: storedFlow.Flow.FlowId, FlowType: storedFlow.Flow.FlowType}
+ logger.Debugw(ctx, "multicast-flow-to-be-deleted",
+ log.Fields{
+ "flow": storedFlow,
+ "flow-id": flow.Id,
+ "device-id": f.deviceHandler.device.Id})
+ //remove from device
+ if err := f.removeFlowFromDevice(ctx, &removeFlowMessage, flow.Id); err != nil {
+ // DKB
+ logger.Errorw(ctx, "failed-to-remove-multicast-flow",
+ log.Fields{
+ "flow-id": flow.Id,
+ "error": err})
+ return
+ }
+ logger.Infow(ctx, "multicast-flow-removed-from-device-successfully", log.Fields{"flow-id": 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 {
+ logger.Errorw(ctx, "failed-to-delete-multicast-flow-from-the-kv-store",
+ log.Fields{"flow": storedFlow,
+ "err": err})
+ return
+ }
+ //release flow id
+ logger.Debugw(ctx, "releasing-multicast-flow-id",
+ log.Fields{"flow-id": 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) error {
+ logger.Infow(ctx, "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()
+ logger.Debugw(ctx, "action-type-output", log.Fields{"out_port": actionInfo[Output].(uint32)})
+ } else {
+ logger.Error(ctx, "invalid-output-port-in-action")
+ return olterrors.NewErrInvalidValue(log.Fields{"invalid-out-port-action": 0}, nil)
+ }
+ }
+ }
+
+ if flows.HasGroup(flow) {
+ direction = Multicast
+ f.clearFlowFromResourceManager(ctx, flow, direction)
+ return nil
+ } else if IsUpstream(actionInfo[Output].(uint32)) {
+ direction = Upstream
+ } else {
+ direction = Downstream
+ }
+
+ _, intfID, onuID, uniID, _, _, err := FlowExtractInfo(ctx, flow, direction)
+ if err != nil {
+ return err
+ }
+
+ userKey := tpLockKey{intfID, onuID, uniID}
+
+ if f.perUserFlowHandleLock.TryLock(userKey) {
+ f.clearFlowFromResourceManager(ctx, flow, direction) //TODO: Take care of the limitations
+ f.perUserFlowHandleLock.Unlock(userKey)
+ } else {
+ // Ideally this should never happen
+ logger.Errorw(ctx, "failed-to-acquire-lock-to-remove-flow--remove-aborted", log.Fields{"flow": flow})
+ return errors.New("failed-to-acquire-per-user-lock")
+ }
+
+ return nil
+}
+
+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 {
+ logger.Debug(ctx, "pending-flow-deletes-completed")
+ ch <- true
+ return
+ }
+ case <-ctx.Done():
+ logger.Error(ctx, "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) error {
+ classifierInfo := make(map[string]interface{})
+ actionInfo := make(map[string]interface{})
+ var UsMeterID uint32
+ var DsMeterID uint32
+
+ logger.Infow(ctx, "adding-flow",
+ log.Fields{
+ "flow": flow,
+ "flowmetadata": flowMetadata})
+ formulateClassifierInfoFromFlow(ctx, classifierInfo, flow)
+
+ err := formulateActionInfoFromFlow(ctx, actionInfo, classifierInfo, flow)
+ if err != nil {
+ // Error logging is already done in the called function
+ // So just return in case of error
+ return err
+ }
+
+ if flows.HasGroup(flow) {
+ // handle multicast flow
+ return f.handleFlowWithGroup(ctx, actionInfo, classifierInfo, flow)
+ }
+
+ /* Controller bound trap flows */
+ err = formulateControllerBoundTrapFlowInfo(ctx, actionInfo, classifierInfo, flow)
+ if err != nil {
+ // error if any, already logged in the called function
+ return err
+ }
+
+ logger.Debugw(ctx, "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 {
+ logger.Info(ctx, "adding-lldp-flow")
+ return f.addLLDPFlow(ctx, flow, portNo)
+ }
+ }
+ 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) {
+ logger.Debug(ctx, "trap-dhcp-from-nni-flow")
+ return f.addDHCPTrapFlowOnNNI(ctx, flow, classifierInfo, portNo)
+ }
+ }
+ }
+ }
+ if isIgmpTrapDownstreamFlow(classifierInfo) {
+ logger.Debug(ctx, "trap-igmp-from-nni-flow")
+ return f.addIgmpTrapFlowOnNNI(ctx, flow, classifierInfo, portNo)
+ }
+
+ f.resourceMgr.AddUniPortToOnuInfo(ctx, intfID, onuID, portNo)
+
+ TpID, err := getTpIDFromFlow(ctx, flow)
+ if err != nil {
+ return olterrors.NewErrNotFound("tpid-for-flow",
+ log.Fields{
+ "flow": flow,
+ "intf-id": IntfID,
+ "onu-id": onuID,
+ "uni-id": uniID}, err)
+ }
+ logger.Debugw(ctx, "tpid-for-this-subcriber",
+ log.Fields{
+ "tp-id": TpID,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID})
+ if IsUpstream(actionInfo[Output].(uint32)) {
+ UsMeterID = flows.GetMeterIdFromFlow(flow)
+ logger.Debugw(ctx, "upstream-flow-meter-id", log.Fields{"us-meter-id": UsMeterID})
+ } else {
+ DsMeterID = flows.GetMeterIdFromFlow(flow)
+ logger.Debugw(ctx, "downstream-flow-meter-id", log.Fields{"ds-meter-id": DsMeterID})
+
+ }
+
+ pnFlDelKey := pendingFlowDeleteKey{intfID, onuID, uniID}
+ if _, ok := f.pendingFlowDelete.Load(pnFlDelKey); !ok {
+ logger.Debugw(ctx, "no-pending-flows-found--going-ahead-with-flow-install",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": 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:
+ logger.Debugw(ctx, "all-pending-flow-deletes-completed",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID})
+ f.divideAndAddFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, uint32(TpID), UsMeterID, DsMeterID, flowMetadata)
+
+ case <-time.After(10 * time.Second):
+ return olterrors.NewErrTimeout("pending-flow-deletes",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID}, nil)
+ }
+ }
+
+ logger.Debugw(ctx, "end-adding-flow",
+ log.Fields{
+ "flow": flow,
+ "flowmetadata": flowMetadata})
+
+ return nil
+}
+
+// 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
+ logger.Debugw(ctx, "add-multicast-flow", log.Fields{
+ "classifier-info": classifierInfo,
+ "actionInfo": actionInfo})
+
+ networkInterfaceID, err := f.getNNIInterfaceIDOfMulticastFlow(ctx, classifierInfo)
+ if err != nil {
+ return olterrors.NewErrNotFound("multicast-in-port", log.Fields{"classifier": classifierInfo}, err)
+ }
+ mcastFlowClassificationByEthDst := false
+
+ if mcastFlowClassificationByEthDst {
+ //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)
+ classifierInfo[EthDst] = multicastMac
+ logger.Debugw(ctx, "multicast-ip-to-mac-conversion-success",
+ log.Fields{
+ "ip:": ipv4Dst.(uint32),
+ "mac:": multicastMac})
+ }
+ }
+ delete(classifierInfo, EthType)
+
+ onuID := NoneOnuID
+ uniID := NoneUniID
+ gemPortID := NoneGemPortID
+
+ flowStoreCookie := getFlowStoreCookie(ctx, classifierInfo, uint32(0))
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ logger.Infow(ctx, "multicast-flow-exists-not-re-adding", log.Fields{"classifier-info": classifierInfo})
+ return nil
+ }
+ flowID, err := f.resourceMgr.GetFlowID(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), uint32(gemPortID), flowStoreCookie, "", 0, 0)
+ if err != nil {
+ return olterrors.NewErrNotFound("multicast-flow-id",
+ log.Fields{
+ "interface-id": networkInterfaceID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "gem-port-id": gemPortID,
+ "cookie": flowStoreCookie},
+ err)
+ }
+ classifierProto, err := makeOpenOltClassifierField(classifierInfo)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"classifier": classifierInfo}, err)
+ }
+ 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 olterrors.NewErrFlowOp("add", flowID, log.Fields{"flow": multicastFlow}, err)
+ }
+ logger.Info(ctx, "multicast-flow-added-to-device-successfully")
+ if group, _, err := f.GetFlowGroupFromKVStore(ctx, groupID, true); err == nil {
+ //calling groupAdd to set group members after multicast flow creation
+ if err := f.ModifyGroup(ctx, group); err != nil {
+ return olterrors.NewErrGroupOp("modify", groupID, log.Fields{"group": group}, err)
+ }
+ //cached group can be removed now
+ if err := f.resourceMgr.RemoveFlowGroupFromKVStore(ctx, groupID, true); err != nil {
+ logger.Warnw(ctx, "failed-to-remove-flow-group", log.Fields{"group-id": groupID, "error": err})
+ }
+ }
+
+ 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 olterrors.NewErrPersistence("update", "flow", flowID, log.Fields{"flow": multicastFlow}, err)
+ }
+ return nil
+}
+
+//getNNIInterfaceIDOfMulticastFlow returns associated NNI interface id of the inPort criterion if exists; returns the first NNI interface of the device otherwise
+func (f *OpenOltFlowMgr) getNNIInterfaceIDOfMulticastFlow(ctx context.Context, classifierInfo map[string]interface{}) (uint32, error) {
+ if inPort, ok := classifierInfo[InPort]; ok {
+ nniInterfaceID, err := IntfIDFromNniPortNum(ctx, inPort.(uint32))
+ if err != nil {
+ return 0, olterrors.NewErrInvalidValue(log.Fields{"nni-in-port-number": inPort}, err)
+ }
+ return nniInterfaceID, nil
+ }
+ nniPorts, e := f.resourceMgr.GetNNIFromKVStore(ctx)
+ if e == nil && len(nniPorts) > 0 {
+ return nniPorts[0], nil
+ }
+ return 0, olterrors.NewErrNotFound("nni-port", nil, e).Log()
+}
+
+// AddGroup add or update the group
+func (f *OpenOltFlowMgr) AddGroup(ctx context.Context, group *ofp.OfpGroupEntry) error {
+ logger.Infow(ctx, "add-group", log.Fields{"group": group})
+ if group == nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"group": group}, nil)
+ }
+
+ groupToOlt := openoltpb2.Group{
+ GroupId: group.Desc.GroupId,
+ Command: openoltpb2.Group_SET_MEMBERS,
+ Action: f.buildGroupAction(),
+ }
+
+ logger.Debugw(ctx, "sending-group-to-device", log.Fields{"groupToOlt": groupToOlt})
+ _, err := f.deviceHandler.Client.PerformGroupOperation(ctx, &groupToOlt)
+ if err != nil {
+ return olterrors.NewErrAdapter("add-group-operation-failed", log.Fields{"groupToOlt": groupToOlt}, err)
+ }
+ if err := f.resourceMgr.AddFlowGroupToKVStore(ctx, group, true); err != nil {
+ return olterrors.NewErrPersistence("add", "flow-group", group.Desc.GroupId, log.Fields{"group": group}, err)
+ }
+ logger.Infow(ctx, "add-group-operation-performed-on-the-device-successfully ", log.Fields{"groupToOlt": groupToOlt})
+ return nil
+}
+
+// DeleteGroup deletes a group from the device
+func (f *OpenOltFlowMgr) DeleteGroup(ctx context.Context, group *ofp.OfpGroupEntry) error {
+ logger.Debugw(ctx, "delete-group", log.Fields{"group": group})
+ if group == nil {
+ logger.Error(ctx, "unable-to-delete-group--invalid-argument--group-is-nil")
+ return olterrors.NewErrInvalidValue(log.Fields{"group": group}, nil)
+ }
+
+ groupToOlt := openoltpb2.Group{
+ GroupId: group.Desc.GroupId,
+ }
+
+ logger.Debugw(ctx, "deleting-group-from-device", log.Fields{"groupToOlt": groupToOlt})
+ _, err := f.deviceHandler.Client.DeleteGroup(ctx, &groupToOlt)
+ if err != nil {
+ logger.Errorw(ctx, "delete-group-failed-on-dev", log.Fields{"groupToOlt": groupToOlt, "err": err})
+ return olterrors.NewErrAdapter("delete-group-operation-failed", log.Fields{"groupToOlt": groupToOlt}, err)
+ }
+ if err := f.resourceMgr.RemoveFlowGroupFromKVStore(ctx, group.Desc.GroupId, false); err != nil {
+ return olterrors.NewErrPersistence("delete", "flow-group", group.Desc.GroupId, log.Fields{"group": group}, err)
+ }
+ logger.Debugw(ctx, "delete-group-operation-performed-on-the-device-successfully ", log.Fields{"groupToOlt": groupToOlt})
+ return nil
+}
+
+//buildGroupAction creates and returns a group action
+func (f *OpenOltFlowMgr) buildGroupAction() *openoltpb2.Action {
+ var actionCmd openoltpb2.ActionCmd
+ var action openoltpb2.Action
+ action.Cmd = &actionCmd
+ action.Cmd.RemoveOuterTag = true
+ return &action
+}
+
+// ModifyGroup updates the group
+func (f *OpenOltFlowMgr) ModifyGroup(ctx context.Context, group *ofp.OfpGroupEntry) error {
+ logger.Infow(ctx, "modify-group", log.Fields{"group": group})
+ if group == nil || group.Desc == nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"group": group}, nil)
+ }
+
+ newGroup := f.buildGroup(ctx, group.Desc.GroupId, group.Desc.Buckets)
+ val, groupExists, err := f.GetFlowGroupFromKVStore(ctx, group.Desc.GroupId, false)
+
+ if err != nil {
+ return olterrors.NewErrNotFound("flow-group-in-kv-store", log.Fields{"groupId": group.Desc.GroupId}, err)
+ }
+
+ var current *openoltpb2.Group // represents the group on the device
+ if groupExists {
+ // group already exists
+ current = f.buildGroup(ctx, group.Desc.GroupId, val.Desc.GetBuckets())
+ logger.Debugw(ctx, "modify-group--group exists",
+ log.Fields{
+ "group on the device": val,
+ "new": group})
+ } else {
+ current = f.buildGroup(ctx, group.Desc.GroupId, nil)
+ }
+
+ logger.Debugw(ctx, "modify-group--comparing-current-and-new",
+ log.Fields{
+ "group on the device": current,
+ "new": newGroup})
+ membersToBeAdded := f.findDiff(current, newGroup)
+ membersToBeRemoved := f.findDiff(newGroup, current)
+
+ logger.Infow(ctx, "modify-group--differences found", log.Fields{
+ "membersToBeAdded": membersToBeAdded,
+ "membersToBeRemoved": membersToBeRemoved,
+ "groupId": group.Desc.GroupId})
+
+ groupToOlt := openoltpb2.Group{
+ GroupId: group.Desc.GroupId,
+ }
+ var errAdd, errRemoved error
+ if len(membersToBeAdded) > 0 {
+ groupToOlt.Command = openoltpb2.Group_ADD_MEMBERS
+ groupToOlt.Members = membersToBeAdded
+ //execute addMembers
+ errAdd = f.callGroupAddRemove(ctx, &groupToOlt)
+ }
+ if len(membersToBeRemoved) > 0 {
+ groupToOlt.Command = openoltpb2.Group_REMOVE_MEMBERS
+ groupToOlt.Members = membersToBeRemoved
+ //execute removeMembers
+ errRemoved = f.callGroupAddRemove(ctx, &groupToOlt)
+ }
+
+ if errAdd == nil && errRemoved == nil {
+ if err := f.resourceMgr.AddFlowGroupToKVStore(ctx, group, false); err != nil {
+ return olterrors.NewErrPersistence("add", "flow-group", group.Desc.GroupId, log.Fields{"group": group}, err)
+ }
+ logger.Infow(ctx, "modify-group-was-success--storing-group",
+ log.Fields{
+ "group": group,
+ "existingGroup": current})
+ } else {
+ logger.Warnw(ctx, "one-of-the-group-add/remove-operations-failed--cannot-save-group-modifications",
+ log.Fields{"group": group})
+ if errAdd != nil {
+ return errAdd
+ }
+ return errRemoved
+ }
+ return nil
+}
+
+//callGroupAddRemove performs add/remove buckets operation for the indicated group
+func (f *OpenOltFlowMgr) callGroupAddRemove(ctx context.Context, group *openoltpb2.Group) error {
+ if err := f.performGroupOperation(ctx, group); err != nil {
+ st, _ := status.FromError(err)
+ //ignore already exists error code
+ if st.Code() != codes.AlreadyExists {
+ return olterrors.NewErrGroupOp("groupAddRemove", group.GroupId, log.Fields{"status": st}, err)
+ }
+ }
+ return nil
+}
+
+//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(ctx context.Context, group *openoltpb2.Group) error {
+ logger.Debugw(ctx, "sending-group-to-device",
+ log.Fields{
+ "groupToOlt": group,
+ "command": group.Command})
+ _, err := f.deviceHandler.Client.PerformGroupOperation(log.WithSpanFromContext(context.Background(), ctx), group)
+ if err != nil {
+ return olterrors.NewErrAdapter("group-operation-failed", log.Fields{"groupToOlt": group}, err)
+ }
+ return nil
+}
+
+//buildGroup build openoltpb2.Group from given group id and bucket list
+func (f *OpenOltFlowMgr) buildGroup(ctx context.Context, groupID uint32, buckets []*ofp.OfpBucket) *openoltpb2.Group {
+ group := openoltpb2.Group{
+ GroupId: groupID}
+ for _, ofBucket := range buckets {
+ member := f.buildMember(ctx, 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(ctx context.Context, 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 {
+ logger.Debugw(ctx, "bucket-skipped-since-no-out-port-found-in-it", log.Fields{"ofBucket": ofBucket})
+ return nil
+ }
+ interfaceID := IntfIDFromUniPortNum(outPort)
+ logger.Debugw(ctx, "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
+ }
+ logger.Warnf(ctx, "bucket-skipped-since-interface-2-gem-mapping-cannot-be-found", log.Fields{"ofBucket": ofBucket})
+ return nil
+}
+
+//sendTPDownloadMsgToChild send payload
+func (f *OpenOltFlowMgr) sendTPDownloadMsgToChild(ctx context.Context, intfID uint32, onuID uint32, uniID uint32, uni string, TpID uint32) error {
+
+ onuDev, err := f.getOnuDevice(ctx, intfID, onuID)
+ if err != nil {
+ logger.Errorw(ctx, "couldnt-find-onu-child-device",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID})
+ return err
+ }
+ logger.Debugw(ctx, "got-child-device-from-olt-device-handler", log.Fields{"onu-id": onuDev.deviceID})
+
+ tpPath := f.getTPpath(ctx, intfID, uni, TpID)
+ tpDownloadMsg := &ic.InterAdapterTechProfileDownloadMessage{UniId: uniID, Path: tpPath}
+ logger.Debugw(ctx, "sending-load-tech-profile-request-to-brcm-onu-adapter", log.Fields{"tpDownloadMsg": *tpDownloadMsg})
+ sendErr := f.deviceHandler.AdapterProxy.SendInterAdapterMessage(log.WithSpanFromContext(context.Background(), ctx),
+ tpDownloadMsg,
+ ic.InterAdapterMessageType_TECH_PROFILE_DOWNLOAD_REQUEST,
+ f.deviceHandler.device.Type,
+ onuDev.deviceType,
+ onuDev.deviceID,
+ onuDev.proxyDeviceID, "")
+ if sendErr != nil {
+ return olterrors.NewErrCommunication("send-techprofile-download-request",
+ log.Fields{
+ "from-adapter": f.deviceHandler.device.Type,
+ "to-adapter": onuDev.deviceType,
+ "onu-id": onuDev.deviceID,
+ "proxyDeviceID": onuDev.proxyDeviceID}, sendErr)
+ }
+ logger.Infow(ctx, "success-sending-load-tech-profile-request-to-brcm-onu-adapter", log.Fields{"tpDownloadMsg": *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) error {
+
+ f.onuGemInfoLock.Lock()
+ defer f.onuGemInfoLock.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 {
+ return err
+ }
+ logger.Infow(ctx, "updated-onuinfo",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "serial-num": serialNum,
+ "onu": onu,
+ "device-id": f.deviceHandler.device.Id})
+ return nil
+}
+
+//addGemPortToOnuInfoMap function adds GEMport to ONU map
+func (f *OpenOltFlowMgr) addGemPortToOnuInfoMap(ctx context.Context, intfID uint32, onuID uint32, gemPort uint32) {
+
+ f.onuGemInfoLock.Lock()
+ defer f.onuGemInfoLock.Unlock()
+
+ logger.Infow(ctx, "adding-gem-to-onu-info-map",
+ log.Fields{
+ "gem-port-id": gemPort,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "device-id": f.deviceHandler.device.Id,
+ "onu-gem": f.onuGemInfo[intfID]})
+ onugem := f.onuGemInfo[intfID]
+ 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 {
+ logger.Debugw(ctx, "gem-already-in-cache-no-need-to-update-cache-and-kv-store",
+ log.Fields{
+ "gem": gemPort,
+ "device-id": f.deviceHandler.device.Id})
+ return
+ }
+ }
+ onugem[idx].GemPorts = append(onugem[idx].GemPorts, gemPort)
+ f.onuGemInfo[intfID] = onugem
+ }
+ }
+ err := f.resourceMgr.AddGemToOnuGemInfo(ctx, intfID, onuID, gemPort)
+ if err != nil {
+ logger.Errorw(ctx, "failed-to-add-gem-to-onu",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "gemPort": gemPort,
+ "device-id": f.deviceHandler.device.Id})
+ return
+ }
+ logger.Infow(ctx, "gem-added-to-onu-info-map",
+ log.Fields{
+ "gem-port-id": gemPort,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "device-id": f.deviceHandler.device.Id,
+ "onu-gem": f.onuGemInfo[intfID]})
+}
+
+// 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(ctx context.Context, intfID uint32, gemPortID uint32) (uint32, error) {
+
+ f.onuGemInfoLock.RLock()
+ defer f.onuGemInfoLock.RUnlock()
+
+ logger.Infow(ctx, "getting-onu-id-from-gem-port-and-pon-port",
+ log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "onu-geminfo": f.onuGemInfo[intfID],
+ "intf-id": intfID,
+ "gemport-id": gemPortID})
+
+ onugem := f.onuGemInfo[intfID]
+
+ for _, onu := range onugem {
+ for _, gem := range onu.GemPorts {
+ if gem == gemPortID {
+ return onu.OnuID, nil
+ }
+ }
+ }
+ logger.Errorw(ctx, "onu-id-from-gem-port-not-found", log.Fields{
+ "gem-port-id": gemPortID,
+ "interface-id": intfID,
+ "all-gems-on-port": onugem,
+ })
+ return uint32(0), olterrors.NewErrNotFound("onu-id", log.Fields{
+ "interface-id": intfID,
+ "gem-port-id": gemPortID},
+ nil)
+}
+
+//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(ctx, packetIn.IntfId, packetIn.GemportId); err != nil {
+ // Called method is returning error with all data populated; just return the same
+ return logicalPortNum, err
+ }
+ if packetIn.PortNo != 0 {
+ logicalPortNum = packetIn.PortNo
+ } else {
+ uniID := uint32(0) // FIXME - multi-uni support
+ logicalPortNum = MkUniPortNum(ctx, 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, packetIn.Pkt)
+ } else if packetIn.IntfType == "nni" {
+ logicalPortNum = IntfIDToPortNo(packetIn.IntfId, voltha.Port_ETHERNET_NNI)
+ }
+ logger.Infow(ctx, "retrieved-logicalport-from-packet-in",
+ log.Fields{
+ "logical-port-num": logicalPortNum,
+ "intf-type": 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, packet []byte) (uint32, error) {
+ var gemPortID uint32
+
+ ctag, priority, err := getCTagFromPacket(ctx, packet)
+ if err != nil {
+ return 0, err
+ }
+
+ f.onuGemInfoLock.RLock()
+ defer f.onuGemInfoLock.RUnlock()
+ pktInkey := rsrcMgr.PacketInInfoKey{IntfID: intfID, OnuID: onuID, LogicalPort: portNum, VlanID: ctag, Priority: priority}
+ var ok bool
+ gemPortID, ok = f.packetInGemPort[pktInkey]
+ if ok {
+ logger.Debugw(ctx, "found-gemport-for-pktin-key",
+ log.Fields{
+ "pktinkey": pktInkey,
+ "gem": gemPortID})
+
+ return gemPortID, nil
+ }
+ gemPortID, err = f.resourceMgr.GetGemPortFromOnuPktIn(ctx, pktInkey)
+ if err == nil {
+ if gemPortID != 0 {
+ f.packetInGemPort[pktInkey] = gemPortID
+ logger.Infow(ctx, "found-gem-port-from-kv-store-and-updating-cache-with-gemport",
+ log.Fields{
+ "pktinkey": pktInkey,
+ "gem": gemPortID})
+ return gemPortID, nil
+ }
+ }
+ return uint32(0), olterrors.NewErrNotFound("gem-port",
+ log.Fields{
+ "pktinkey": pktInkey,
+ "gem": gemPortID}, err)
+
+}
+
+// nolint: gocyclo
+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, tpID 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,
+ tpID uint32) error,
+ args map[string]uint32,
+ classifier map[string]interface{}, action map[string]interface{},
+ logicalFlow *ofp.OfpFlowStats,
+ gemPorts []uint32,
+ TpInst interface{},
+ FlowType string,
+ direction string,
+ tpID uint32,
+ vlanID ...uint32) {
+ logger.Debugw(ctx, "installing-flow-on-all-gem-ports",
+ log.Fields{
+ "FlowType": FlowType,
+ "gemPorts": gemPorts,
+ "vlan": vlanID})
+
+
+
+ switch TpInst := TpInst.(type) {
+ case *tp.TechProfile:
+ attributes := TpInst.DownstreamGemPortAttributeList
+ if direction == Upstream {
+ attributes = TpInst.UpstreamGemPortAttributeList
+ }
+
+ for _, gemPortAttribute := range attributes {
+ if direction == Downstream && strings.ToUpper(gemPortAttribute.IsMulticast) == "TRUE" {
+ continue
+ }
+ gemPortID := gemPortAttribute.GemportID
+ if allPbitsMarked(gemPortAttribute.PbitMap) {
+ classifier[VlanPcp] = uint32(VlanPCPMask)
+ if FlowType == DhcpFlow || FlowType == IgmpFlow || FlowType == HsiaFlow {
+ if err := f1(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } else if FlowType == EapolFlow {
+ if err := f2(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, vlanID[0], tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ }
+ } else {
+ for pos, pbitSet := range strings.TrimPrefix(gemPortAttribute.PbitMap, BinaryStringPrefix) {
+ if pbitSet == BinaryBit1 {
+ classifier[VlanPcp] = uint32(len(strings.TrimPrefix(gemPortAttribute.PbitMap, BinaryStringPrefix))) - 1 - uint32(pos)
+ if FlowType == DhcpFlow || FlowType == IgmpFlow || FlowType == HsiaFlow {
+ if err := f1(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } else if FlowType == EapolFlow {
+ if err := f2(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, vlanID[0], tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ }
+ }
+ }
+ }
+ }
+ case *tp.EponProfile:
+ if direction == Upstream {
+ attributes := TpInst.UpstreamQueueAttributeList
+ for _, queueAttribute := range attributes {
+ gemPortID := queueAttribute.GemportID
+ if allPbitsMarked(queueAttribute.PbitMap) {
+ classifier[VlanPcp] = uint32(VlanPCPMask)
+ if FlowType == DhcpFlow || FlowType == IgmpFlow || FlowType == HsiaFlow {
+ if err := f1(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } else if FlowType == EapolFlow {
+ if err := f2(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, vlanID[0], tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ }
+ } else {
+ for pos, pbitSet := range strings.TrimPrefix(queueAttribute.PbitMap, BinaryStringPrefix) {
+ if pbitSet == BinaryBit1 {
+ classifier[VlanPcp] = uint32(len(strings.TrimPrefix(queueAttribute.PbitMap, BinaryStringPrefix))) - 1 - uint32(pos)
+ if FlowType == DhcpFlow || FlowType == IgmpFlow || FlowType == HsiaFlow {
+ if err := f1(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } else if FlowType == EapolFlow {
+ if err := f2(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, vlanID[0], tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ attributes := TpInst.DownstreamQueueAttributeList
+ for _, queueAttribute := range attributes {
+ gemPortID := queueAttribute.GemportID
+ if allPbitsMarked(queueAttribute.PbitMap) {
+ classifier[VlanPcp] = uint32(VlanPCPMask)
+ if FlowType == DhcpFlow || FlowType == IgmpFlow || FlowType == HsiaFlow {
+ if err := f1(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } else if FlowType == EapolFlow {
+ if err := f2(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, vlanID[0], tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ }
+ } else {
+ for pos, pbitSet := range strings.TrimPrefix(queueAttribute.PbitMap, BinaryStringPrefix) {
+ if pbitSet == BinaryBit1 {
+ classifier[VlanPcp] = uint32(len(strings.TrimPrefix(queueAttribute.PbitMap, BinaryStringPrefix))) - 1 - uint32(pos)
+ if FlowType == DhcpFlow || FlowType == IgmpFlow || FlowType == HsiaFlow {
+ if err := f1(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } else if FlowType == EapolFlow {
+ if err := f2(ctx, args["intfId"], args["onuId"], args["uniId"], args["portNo"], classifier, action, logicalFlow, args["allocId"], gemPortID, vlanID[0], tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ default:
+ logger.Errorw(ctx, "unknown-tech", log.Fields{"tpInst": TpInst})
+ }
+}
+
+func allPbitsMarked(pbitMap string) bool {
+ for pos, pBit := range pbitMap {
+ if pos >= 2 && pBit != BinaryBit1 {
+ return false
+ }
+ }
+ return true
+}
+
+func (f *OpenOltFlowMgr) addDHCPTrapFlowOnNNI(ctx context.Context, logicalFlow *ofp.OfpFlowStats, classifier map[string]interface{}, portNo uint32) error {
+ logger.Debug(ctx, "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(ctx, classifier, action)
+ if err != nil {
+ return olterrors.NewErrNotFound("nni-intreface-id",
+ log.Fields{
+ "classifier": classifier,
+ "action": action},
+ err)
+ }
+
+ flowStoreCookie := getFlowStoreCookie(ctx, classifier, uint32(0))
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ logger.Info(ctx, "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 olterrors.NewErrNotFound("dhcp-trap-nni-flow-id",
+ log.Fields{
+ "interface-id": networkInterfaceID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "gem-port-id": gemPortID,
+ "cookie": flowStoreCookie},
+ err)
+ }
+ classifierProto, err := makeOpenOltClassifierField(classifier)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"classifier": classifier}, err)
+ }
+ logger.Debugw(ctx, "created-classifier-proto", log.Fields{"classifier": *classifierProto})
+ actionProto, err := makeOpenOltActionField(action, classifier)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"action": action}, err)
+ }
+ logger.Debugw(ctx, "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 olterrors.NewErrFlowOp("add", flowID, log.Fields{"flow": downstreamflow}, err)
+ }
+ logger.Info(ctx, "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 olterrors.NewErrPersistence("update", "flow", flowID, log.Fields{"flow": downstreamflow}, err)
+ }
+ 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 {
+ logger.Infow(ctx, "adding-igmp-trap-of-nni-flow", log.Fields{"classifier-info": 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(ctx, classifier, action)
+ if err != nil {
+ return olterrors.NewErrNotFound("nni-interface-id", log.Fields{
+ "classifier": classifier,
+ "action": action},
+ err)
+ }
+ flowStoreCookie := getFlowStoreCookie(ctx, classifier, uint32(0))
+ if present := f.resourceMgr.IsFlowCookieOnKVStore(ctx, uint32(networkInterfaceID), int32(onuID), int32(uniID), flowStoreCookie); present {
+ logger.Info(ctx, "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 olterrors.NewErrNotFound("igmp-flow-id",
+ log.Fields{
+ "interface-id": networkInterfaceID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "gem-port-id": gemPortID,
+ "cookie": flowStoreCookie},
+ err)
+ }
+ classifierProto, err := makeOpenOltClassifierField(classifier)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"classifier": classifier}, err)
+ }
+ logger.Debugw(ctx, "created-classifier-proto-for-the-igmp-flow", log.Fields{"classifier": *classifierProto})
+ actionProto, err := makeOpenOltActionField(action, classifier)
+ if err != nil {
+ return olterrors.NewErrInvalidValue(log.Fields{"action": action}, err)
+ }
+ logger.Debugw(ctx, "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 olterrors.NewErrFlowOp("add", flowID, log.Fields{"flow": downstreamflow}, err)
+ }
+ logger.Info(ctx, "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 olterrors.NewErrPersistence("update", "flow", flowID, log.Fields{"flow": downstreamflow}, err)
+ }
+ return nil
+}
+
+func verifyMeterIDAndGetDirection(MeterID uint32, Dir tp_pb.Direction) (string, error) {
+ if MeterID == 0 { // This should never happen
+ return "", olterrors.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
+}
+
+// nolint: gocyclo
+func (f *OpenOltFlowMgr) checkAndAddFlow(ctx context.Context, args map[string]uint32, classifierInfo map[string]interface{},
+ actionInfo map[string]interface{}, flow *ofp.OfpFlowStats, TpInst interface{}, gemPorts []uint32,
+ tpID uint32, uni string) {
+ var gemPort uint32
+ intfID := args[IntfID]
+ onuID := args[OnuID]
+ uniID := args[UniID]
+ portNo := args[PortNo]
+ allocID := args[AllocID]
+ if ipProto, ok := classifierInfo[IPProto]; ok {
+ if ipProto.(uint32) == IPProtoDhcp {
+ logger.Infow(ctx, "adding-dhcp-flow", log.Fields{
+ "tp-id": tpID,
+ "alloc-id": allocID,
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ })
+ if pcp, ok := classifierInfo[VlanPcp]; ok {
+ gemPort = f.techprofile[intfID].GetGemportIDForPbit(ctx, TpInst,
+ tp_pb.Direction_UPSTREAM,
+ pcp.(uint32))
+ //Adding DHCP upstream flow
+
+ if err := f.addDHCPTrapFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } else {
+ //Adding DHCP upstream flow to all gemports
+ installFlowOnAllGemports(ctx, f.addDHCPTrapFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, DhcpFlow, Upstream, tpID)
+ }
+
+ } else if ipProto.(uint32) == IgmpProto {
+ logger.Infow(ctx, "adding-us-igmp-flow",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "classifier-info:": classifierInfo})
+ if pcp, ok := classifierInfo[VlanPcp]; ok {
+ gemPort = f.techprofile[intfID].GetGemportIDForPbit(ctx, TpInst,
+ tp_pb.Direction_UPSTREAM,
+ pcp.(uint32))
+ if err := f.addIGMPTrapFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } else {
+ //Adding IGMP upstream flow to all gem ports
+ installFlowOnAllGemports(ctx, f.addIGMPTrapFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, IgmpFlow, Upstream, tpID)
+ }
+ } else {
+ logger.Errorw(ctx, "invalid-classifier-to-handle", log.Fields{"classifier": classifierInfo, "action": actionInfo})
+ return
+ }
+ } else if ethType, ok := classifierInfo[EthType]; ok {
+ if ethType.(uint32) == EapEthType {
+ logger.Infow(ctx, "adding-eapol-flow", log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ })
+ 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(ctx, TpInst,
+ tp_pb.Direction_UPSTREAM,
+ pcp.(uint32))
+
+ if err := f.addEAPOLFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort, vlanID, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } else {
+ installFlowOnAllGemports(ctx, nil, f.addEAPOLFlow, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, EapolFlow, Upstream, tpID, vlanID)
+ }
+ }
+ } else if _, ok := actionInfo[PushVlan]; ok {
+ logger.Infow(ctx, "adding-upstream-data-rule", log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ })
+ if pcp, ok := classifierInfo[VlanPcp]; ok {
+ gemPort = f.techprofile[intfID].GetGemportIDForPbit(ctx, TpInst,
+ tp_pb.Direction_UPSTREAM,
+ pcp.(uint32))
+ //Adding HSIA upstream flow
+ if err := f.addUpstreamDataFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } else {
+ //Adding HSIA upstream flow to all gemports
+ installFlowOnAllGemports(ctx, f.addUpstreamDataFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, HsiaFlow, Upstream, tpID)
+ }
+ } else if _, ok := actionInfo[PopVlan]; ok {
+ logger.Infow(ctx, "adding-downstream-data-rule", log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ })
+ if pcp, ok := classifierInfo[VlanPcp]; ok {
+ gemPort = f.techprofile[intfID].GetGemportIDForPbit(ctx, TpInst,
+ tp_pb.Direction_DOWNSTREAM,
+ pcp.(uint32))
+ //Adding HSIA downstream flow
+ if err := f.addDownstreamDataFlow(ctx, intfID, onuID, uniID, portNo, classifierInfo, actionInfo, flow, allocID, gemPort, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ } else {
+ //Adding HSIA downstream flow to all gemports
+ installFlowOnAllGemports(ctx, f.addDownstreamDataFlow, nil, args, classifierInfo, actionInfo, flow, gemPorts, TpInst, HsiaFlow, Downstream, tpID)
+ }
+ } else {
+ logger.Errorw(ctx, "invalid-flow-type-to-handle",
+ log.Fields{
+ "intf-id": intfID,
+ "onu-id": onuID,
+ "uni-id": uniID,
+ "classifier": classifierInfo,
+ "action": actionInfo,
+ "flow": flow})
+ return
+ }
+ if noticeDownloadRequest {
+ go func() {
+ if err := f.sendTPDownloadMsgToChild(ctx, intfID, onuID, uniID, uni, tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ }()
+ }
+}
+
+func (f *OpenOltFlowMgr) isGemPortUsedByAnotherFlow(ctx context.Context, gemPK gemPortKey) (bool, error) {
+ if f.perGemPortLock.TryLock(gemPK) {
+ flowIDList := f.flowsUsedByGemPort[gemPK]
+ f.perGemPortLock.Unlock(gemPK)
+ return len(flowIDList) > 1, nil
+ }
+ logger.Error(ctx, "failed-to-acquire-per-gem-port-lock",
+ log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "key": gemPK,
+ })
+ return false, olterrors.NewErrAdapter("failed-to-acquire-per-gem-port-lock", log.Fields{
+ "device-id": f.deviceHandler.device.Id,
+ "key": gemPK,
+ }, nil)
+}
+
+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.
+ if err := f.resourceMgr.RemoveTechProfileIDForOnu(ctx, ponIntf, uint32(onuID), uint32(uniID), tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+ if err := f.DeleteTechProfileInstance(ctx, ponIntf, uint32(onuID), uint32(uniID), "", tpID); err != nil {
+ logger.Warn(ctx, err)
+ }
+
+ // 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).([]tp.TechProfile)
+ logger.Debugw(ctx, "got-single-instance-tp-instances", log.Fields{"tp-instances": tpInstances})
+ for i := 0; i < len(tpInstances); i++ {
+ tpI := tpInstances[i]
+ tpGemPorts := tpI.UpstreamGemPortAttributeList
+ for _, tpGemPort := range tpGemPorts {
+ if tpGemPort.GemportID != gemPortID {
+ logger.Debugw(ctx, "single-instance-tp-is-in-use-by-gem", log.Fields{"gemPort": tpGemPort.GemportID})
+ return true, tpGemPort.GemportID
+ }
+ }
+ }
+ }
+ logger.Debug(ctx, "tech-profile-is-not-in-use-by-any-gem")
+ return false, 0
+}
+
+func formulateClassifierInfoFromFlow(ctx context.Context, classifierInfo map[string]interface{}, flow *ofp.OfpFlowStats) {
+ for _, field := range flows.GetOfbFields(flow) {
+ if field.Type == flows.ETH_TYPE {
+ classifierInfo[EthType] = field.GetEthType()
+ logger.Debug(ctx, "field-type-eth-type", log.Fields{"classifierInfo[ETH_TYPE]": classifierInfo[EthType].(uint32)})
+ } else if field.Type == flows.ETH_DST {
+ classifierInfo[EthDst] = field.GetEthDst()
+ logger.Debug(ctx, "field-type-eth-type", log.Fields{"classifierInfo[ETH_DST]": classifierInfo[EthDst].([]uint8)})
+ } else if field.Type == flows.IP_PROTO {
+ classifierInfo[IPProto] = field.GetIpProto()
+ logger.Debug(ctx, "field-type-ip-proto", log.Fields{"classifierInfo[IP_PROTO]": classifierInfo[IPProto].(uint32)})
+ } else if field.Type == flows.IN_PORT {
+ classifierInfo[InPort] = field.GetPort()
+ logger.Debug(ctx, "field-type-in-port", log.Fields{"classifierInfo[IN_PORT]": classifierInfo[InPort].(uint32)})
+ } else if field.Type == flows.VLAN_VID {
+ classifierInfo[VlanVid] = field.GetVlanVid() & 0xfff
+ logger.Debug(ctx, "field-type-vlan-vid", log.Fields{"classifierInfo[VLAN_VID]": classifierInfo[VlanVid].(uint32)})
+ } else if field.Type == flows.VLAN_PCP {
+ classifierInfo[VlanPcp] = field.GetVlanPcp()
+ logger.Debug(ctx, "field-type-vlan-pcp", log.Fields{"classifierInfo[VLAN_PCP]": classifierInfo[VlanPcp].(uint32)})
+ } else if field.Type == flows.UDP_DST {
+ classifierInfo[UDPDst] = field.GetUdpDst()
+ logger.Debug(ctx, "field-type-udp-dst", log.Fields{"classifierInfo[UDP_DST]": classifierInfo[UDPDst].(uint32)})
+ } else if field.Type == flows.UDP_SRC {
+ classifierInfo[UDPSrc] = field.GetUdpSrc()
+ logger.Debug(ctx, "field-type-udp-src", log.Fields{"classifierInfo[UDP_SRC]": classifierInfo[UDPSrc].(uint32)})
+ } else if field.Type == flows.IPV4_DST {
+ classifierInfo[Ipv4Dst] = field.GetIpv4Dst()
+ logger.Debug(ctx, "field-type-ipv4-dst", log.Fields{"classifierInfo[IPV4_DST]": classifierInfo[Ipv4Dst].(uint32)})
+ } else if field.Type == flows.IPV4_SRC {
+ classifierInfo[Ipv4Src] = field.GetIpv4Src()
+ logger.Debug(ctx, "field-type-ipv4-src", log.Fields{"classifierInfo[IPV4_SRC]": classifierInfo[Ipv4Src].(uint32)})
+ } else if field.Type == flows.METADATA {
+ classifierInfo[Metadata] = field.GetTableMetadata()
+ logger.Debug(ctx, "field-type-metadata", log.Fields{"classifierInfo[Metadata]": classifierInfo[Metadata].(uint64)})
+ } else if field.Type == flows.TUNNEL_ID {
+ classifierInfo[TunnelID] = field.GetTunnelId()
+ logger.Debug(ctx, "field-type-tunnelId", log.Fields{"classifierInfo[TUNNEL_ID]": classifierInfo[TunnelID].(uint64)})
+ } else {
+ logger.Errorw(ctx, "un-supported-field-type", log.Fields{"type": field.Type})
+ return
+ }
+ }
+}
+
+func formulateActionInfoFromFlow(ctx context.Context, 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()
+ logger.Debugw(ctx, "action-type-output", log.Fields{"out-port": actionInfo[Output].(uint32)})
+ } else {
+ return olterrors.NewErrInvalidValue(log.Fields{"output-port": nil}, nil)
+ }
+ } else if action.Type == flows.POP_VLAN {
+ actionInfo[PopVlan] = true
+ logger.Debugw(ctx, "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 {
+ logger.Errorw(ctx, "invalid ethertype in push action", log.Fields{"ethertype": actionInfo[PushVlan].(int32)})
+ } else {
+ actionInfo[PushVlan] = true
+ actionInfo[TPID] = tpid
+ logger.Debugw(ctx, "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 olterrors.NewErrInvalidValue(log.Fields{"openflow-class": ofClass}, nil)
+ }
+ /*logger.Debugw(ctx, "action-type-set-field",log.Fields{"field": field, "in_port": classifierInfo[IN_PORT].(uint32)})*/
+ formulateSetFieldActionInfoFromFlow(ctx, field, actionInfo)
+ }
+ }
+ } else if action.Type == flows.GROUP {
+ formulateGroupActionInfoFromFlow(ctx, action, actionInfo)
+ } else {
+ return olterrors.NewErrInvalidValue(log.Fields{"action-type": action.Type}, nil)
+ }
+ }
+ return nil
+}
+
+func formulateSetFieldActionInfoFromFlow(ctx context.Context, field *ofp.OfpOxmField, actionInfo map[string]interface{}) {
+ if ofbField := field.GetOfbField(); ofbField != nil {
+ fieldtype := ofbField.GetType()
+ if fieldtype == ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID {
+ if vlan := ofbField.GetVlanVid(); vlan != 0 {
+ actionInfo[VlanVid] = vlan & 0xfff
+ logger.Debugw(ctx, "action-set-vlan-vid", log.Fields{"actionInfo[VLAN_VID]": actionInfo[VlanVid].(uint32)})
+ } else {
+ logger.Error(ctx, "no-invalid-vlan-id-in-set-vlan-vid-action")
+ }
+ } else if fieldtype == ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP {
+ pcp := ofbField.GetVlanPcp()
+ actionInfo[VlanPcp] = pcp
+ logger.Debugw(ctx, "action-set-vlan-pcp", log.Fields{"actionInfo[VLAN_PCP]": actionInfo[VlanPcp].(uint32)})
+ } else {
+ logger.Errorw(ctx, "unsupported-action-set-field-type", log.Fields{"type": fieldtype})
+ }
+ }
+}
+
+func formulateGroupActionInfoFromFlow(ctx context.Context, action *ofp.OfpAction, actionInfo map[string]interface{}) {
+ if action.GetGroup() == nil {
+ logger.Warn(ctx, "no-group-entry-found-in-the-group-action")
+ } else {
+ actionInfo[GroupID] = action.GetGroup().GroupId
+ logger.Debugw(ctx, "action-group-id", log.Fields{"actionInfo[GroupID]": actionInfo[GroupID].(uint32)})
+ }
+}
+
+func formulateControllerBoundTrapFlowInfo(ctx context.Context, actionInfo, classifierInfo map[string]interface{}, flow *ofp.OfpFlowStats) error {
+ if isControllerFlow := IsControllerBoundFlow(actionInfo[Output].(uint32)); isControllerFlow {
+ logger.Debug(ctx, "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
+ logger.Debugw(ctx, "upstream-pon-to-controller-flow--inport-in-tunnelid",
+ log.Fields{
+ "newinport": classifierInfo[InPort].(uint32),
+ "outport": actionInfo[Output].(uint32)})
+ } else {
+ return olterrors.NewErrNotFound("child-in-port",
+ log.Fields{
+ "reason": "upstream-pon-to-controller-flow--no-inport-in-tunnelid",
+ "flow": flow}, nil)
+ }
+ }
+ } else {
+ logger.Debug(ctx, "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
+ logger.Debugw(ctx, "downstream-nni-to-pon-port-flow, outport-in-tunnelid",
+ log.Fields{
+ "newoutport": actionInfo[Output].(uint32),
+ "outport": actionInfo[Output].(uint32)})
+ } else {
+ return olterrors.NewErrNotFound("out-port",
+ log.Fields{
+ "reason": "downstream-nni-to-pon-port-flow--no-outport-in-tunnelid",
+ "flow": flow}, nil)
+ }
+ // 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
+ logger.Debugw(ctx, "upstream-pon-to-nni-port-flow, inport-in-tunnelid",
+ log.Fields{
+ "newinport": actionInfo[Output].(uint32),
+ "outport": actionInfo[Output].(uint32)})
+ } else {
+ return olterrors.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)
+ }
+ }
+ }
+ return nil
+}
+
+func getTpIDFromFlow(ctx context.Context, 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(ctx, flow)
+ if metadata == 0 {
+ return 0, olterrors.NewErrNotFound("metadata", log.Fields{"flow": flow}, nil)
+ }
+ TpID := flows.GetTechProfileIDFromWriteMetaData(ctx, 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(ctx context.Context, classifier map[string]interface{}, action map[string]interface{}) (uint32, error) {
+
+ portType := IntfIDToPortTypeName(classifier[InPort].(uint32))
+ if portType == voltha.Port_PON_OLT {
+ intfID, err := IntfIDFromNniPortNum(ctx, action[Output].(uint32))
+ if err != nil {
+ logger.Debugw(ctx, "invalid-action-port-number",
+ log.Fields{
+ "port-number": action[Output].(uint32),
+ "error": err})
+ return uint32(0), err
+ }
+ logger.Infow(ctx, "output-nni-intfId-is", log.Fields{"intf-id": intfID})
+ return intfID, nil
+ } else if portType == voltha.Port_ETHERNET_NNI {
+ intfID, err := IntfIDFromNniPortNum(ctx, classifier[InPort].(uint32))
+ if err != nil {
+ logger.Debugw(ctx, "invalid-classifier-port-number",
+ log.Fields{
+ "port-number": action[Output].(uint32),
+ "error": err})
+ return uint32(0), err
+ }
+ logger.Infow(ctx, "input-nni-intfId-is", log.Fields{"intf-id": 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, pkt []byte) {
+ cTag, priority, err := getCTagFromPacket(ctx, pkt)
+ if err != nil {
+ logger.Errorw(ctx, "unable-to-update-gem-port-for-packet-in",
+ log.Fields{"intfID": intfID, "onuID": onuID, "logicalPort": logicalPort, "gemPort": gemPort, "err": err})
+ return
+ }
+ pktInkey := rsrcMgr.PacketInInfoKey{IntfID: intfID, OnuID: onuID, LogicalPort: logicalPort, VlanID: cTag, Priority: priority}
+
+ f.onuGemInfoLock.Lock()
+ defer f.onuGemInfoLock.Unlock()
+
+ lookupGemPort, ok := f.packetInGemPort[pktInkey]
+ if ok {
+ if lookupGemPort == gemPort {
+ logger.Infow(ctx, "pktin-key/value-found-in-cache--no-need-to-update-kv--assume-both-in-sync",
+ log.Fields{
+ "pktinkey": pktInkey,
+ "gem": gemPort})
+ return
+ }
+ }
+ f.packetInGemPort[pktInkey] = gemPort
+
+ f.resourceMgr.UpdateGemPortForPktIn(ctx, pktInkey, gemPort)
+ logger.Infow(ctx, "pktin-key-not-found-in-local-cache-value-is-different--updating-cache-and-kv-store",
+ log.Fields{
+ "pktinkey": pktInkey,
+ "gem": gemPort})
+}
+
+//getCTagFromPacket retrieves and returns c-tag and priority value from a packet.
+func getCTagFromPacket(ctx context.Context, packet []byte) (uint16, uint8, error) {
+ if packet == nil || len(packet) < 18 {
+ logger.Error(ctx, "unable-get-c-tag-from-the-packet--invalid-packet-length ")
+ return 0, 0, errors.New("invalid packet length")
+ }
+ outerEthType := (uint16(packet[12]) << 8) | uint16(packet[13])
+ innerEthType := (uint16(packet[16]) << 8) | uint16(packet[17])
+
+ var index int8
+ if outerEthType == 0x8100 {
+ if innerEthType == 0x8100 {
+ // q-in-q 802.1ad or 802.1q double tagged packet.
+ // get the inner vlanId
+ index = 18
+ } else {
+ index = 14
+ }
+ priority := (packet[index] >> 5) & 0x7
+ //13 bits composes vlanId value
+ vlan := ((uint16(packet[index]) << 8) & 0x0fff) | uint16(packet[index+1])
+ return vlan, priority, nil
+ }
+ logger.Debugf(ctx, "No vlanId found in the packet. Returning zero as c-tag")
+ return 0, 0, nil
+}
+
+// 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.onuGemInfoLock.Lock()
+ defer f.onuGemInfoLock.Unlock()
+
+ onugem := f.onuGemInfo[intfID]
+ for idx, onu := range onugem {
+ if onu.OnuID == onuID {
+ for _, uni := range onu.UniPorts {
+ if uni == portNum {
+ logger.Infow(ctx, "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 {
+ logger.Error(ctx, "failed-to-get-flowid-list-per-gem", log.Fields{"intf": intf})
+ return
+ }
+ for gem, FlowIDs := range flowIDsList {
+ gemPK := gemPortKey{intf, uint32(gem)}
+ if f.perGemPortLock.TryLock(gemPK) {
+ f.flowsUsedByGemPort[gemPK] = FlowIDs
+ f.perGemPortLock.Unlock(gemPK)
+ } else {
+ logger.Error(ctx, "failed-to-acquire-per-gem-port-lock",
+ log.Fields{
+ "intf-id": intf,
+ "device-id": f.deviceHandler.device.Id,
+ "key": gemPK,
+ })
+ }
+ }
+}
+
+//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 {
+ logger.Error(ctx, "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, olterrors.NewErrNotFound("flow-group", log.Fields{"group-id": groupID}, err)
+ }
+ 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
+}
+
+// L2oamCreateSchedulerQueues creates scheduler queues
+func (f *OpenOltFlowMgr) L2oamCreateSchedulerQueues(ctx context.Context, sq schedQueue) error {
+ if !autoAddFlow {
+ return nil
+ }
+
+ var meterConfig *ofp.OfpMeterConfig
+ if sq.flowMetadata != nil {
+ for _, meter := range sq.flowMetadata.Meters {
+ if sq.meterID == meter.MeterId {
+ meterConfig = meter
+ logger.Debugw(ctx, "found-meter-config-from-flowmetadata",
+ log.Fields{"meterConfig": meterConfig,
+ "device-id": f.deviceHandler.device.Id})
+ break
+ }
+ }
+ } else {
+ logger.Errorw(ctx, "flow-metadata-not-present-in-flow", log.Fields{"device-id": f.deviceHandler.device.Id})
+ }
+ if meterConfig == nil {
+ return olterrors.NewErrNotFound("meterbands", log.Fields{
+ "reason": "Could-not-get-meterbands-from-flowMetadata",
+ "flow-metadata": sq.flowMetadata,
+ "meter-id": sq.meterID,
+ "device-id": f.deviceHandler.device.Id}, nil)
+ } else if len(meterConfig.Bands) < MaxMeterBand {
+ logger.Errorw(ctx, "invalid-number-of-bands-in-meter",
+ log.Fields{"Bands": meterConfig.Bands,
+ "meter-id": sq.meterID,
+ "device-id": f.deviceHandler.device.Id})
+ return olterrors.NewErrInvalidValue(log.Fields{
+ "reason": "Invalid-number-of-bands-in-meter",
+ "meterband-count": len(meterConfig.Bands),
+ "metabands": meterConfig.Bands,
+ "meter-id": sq.meterID,
+ "device-id": f.deviceHandler.device.Id}, nil)
+ }
+ cir := meterConfig.Bands[0].Rate
+ eir := meterConfig.Bands[1].Rate
+ pir := cir + eir
+ onuKey := f.deviceHandler.formOnuKey(sq.intfID, sq.onuID)
+ onuDev, ok := f.deviceHandler.onus.Load(onuKey)
+ if !ok {
+ logger.Errorw(ctx, "not-found-onu-device",
+ log.Fields{"intf-id": sq.intfID,
+ "onu-id": sq.onuID})
+ } else {
+ onuDeviceID := (onuDev.(*OnuDevice)).deviceID
+ if _, exists := mapCreateScheduler[onuDeviceID]; exists {
+ logger.Debugw(ctx, "scheduler-exists",
+ log.Fields{"onu-device-id": onuDeviceID})
+ return nil
+ }
+ mapCreateScheduler[onuDeviceID] = true
+ l2oamCmd := &L2oamCmd{
+ Type: "add-flow",
+ Cir: []byte{0x00, 0x00, 0x00, 0x00},
+ Pir: []byte{0x00, 0x00, 0x00, 0x00},
+ OnuDeviceID: onuDeviceID,
+ }
+ binary.BigEndian.PutUint32(l2oamCmd.Cir[0:4], cir)
+ binary.BigEndian.PutUint32(l2oamCmd.Pir[0:4], pir)
+ L2oamAddFlow(ctx, f.deviceHandler, l2oamCmd)
+
+ f.deviceHandler.L2oamAddFlowAndMount(ctx, onuDeviceID, []byte{0x0f, 0xfb}, []byte{0x00, 0x64})
+ }
+
+ return nil
+}
+
+// l2oamAddEapolToDevice adds EAPOL flow to device
+/*
+func (f *OpenOltFlowMgr) l2oamAddEapolToDevice(ctx context.Context, intfID uint32, onuID uint32) error {
+ if !autoAddFlow {
+ return nil
+ }
+
+ onuKey := f.deviceHandler.formOnuKey(intfID, onuID)
+ onuDev, ok := f.deviceHandler.onus.Load(onuKey)
+ if !ok {
+ logger.Errorw(ctx, "not-found-onu-device",
+ log.Fields{"intf-id": intfID,
+ "onu-id": onuID})
+ } else {
+ f.deviceHandler.L2oamAddFlowAndMount(ctx, (onuDev.(*OnuDevice)).deviceID, []byte{0x0f, 0xfb}, []byte{0x00, 0x64})
+ }
+
+ return nil
+}
+*/
+
+func (f *OpenOltFlowMgr) l2oamAddFlowDevice(ctx context.Context, intfID uint32, onuID uint32, ovid uint32, ivid uint32) {
+ logger.Debugw(ctx, "add-flow-to-device",
+ log.Fields{"onu-id": onuID, "o-vid": ovid, "i-vid": ivid})
+
+ if !autoAddFlow {
+ return
+ }
+
+ onuKey := f.deviceHandler.formOnuKey(intfID, onuID)
+ onuDev, ok := f.deviceHandler.onus.Load(onuKey)
+ if !ok {
+ logger.Errorw(ctx, "not-found-onu-device",
+ log.Fields{"intf-id": intfID,
+ "onu-id": onuID})
+ } else {
+ onuDeviceID := (onuDev.(*OnuDevice)).deviceID
+ if _, exists := mapAddFlow[onuDeviceID]; exists {
+ logger.Debugw(ctx, "flow is added already",
+ log.Fields{"onu-device-id": onuDeviceID})
+ return
+ }
+ mapAddFlow[onuDeviceID] = true
+ ovidBytes := []byte{0x00, 0x00}
+ binary.BigEndian.PutUint16(ovidBytes[:], uint16(ovid))
+ ividBytes := []byte{0x00, 0x00}
+ binary.BigEndian.PutUint16(ividBytes[:], uint16(ivid))
+ f.deviceHandler.L2oamAddFlowAndMount(ctx, onuDeviceID, ovidBytes, ividBytes)
+ }
+}
diff --git a/internal/pkg/core/openolt_flowmgr_test.go b/internal/pkg/core/openolt_flowmgr_test.go
new file mode 100644
index 0000000..4b70e41
--- /dev/null
+++ b/internal/pkg/core/openolt_flowmgr_test.go
@@ -0,0 +1,1116 @@
+/*
+ * 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 (
+ "context"
+ "encoding/hex"
+ "fmt"
+ "reflect"
+ "strconv"
+ "sync"
+ "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, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+ Technology: "Default",
+ },
+ }
+
+ deviceinfo := &openolt.DeviceInfo{Vendor: "openolt", Model: "openolt", HardwareVersion: "1.0", FirmwareVersion: "1.0",
+ DeviceId: "olt", DeviceSerialNumber: "openolt", PonPorts: 16, 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) {
+
+ 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},
+ }
+ 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) {
+
+ 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"
+ 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_createTcontGemports(t *testing.T) {
+ 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 {
+ intfID uint32
+ onuID uint32
+ uniID uint32
+ uni string
+ uniPort uint32
+ TpID uint32
+ UsMeterID uint32
+ DsMeterID uint32
+ flowMetadata *voltha.FlowMetadata
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ {"createTcontGemports-1", args{intfID: 0, onuID: 1, uniID: 1, uni: "16", uniPort: 1, TpID: 64, UsMeterID: 1, DsMeterID: 1, flowMetadata: flowmetadata}},
+ {"createTcontGemports-1", args{intfID: 0, onuID: 1, uniID: 1, uni: "16", uniPort: 1, TpID: 65, UsMeterID: 1, DsMeterID: 1, 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) {
+ _, _, tpInst := flowMgr.createTcontGemports(ctx, tt.args.intfID, tt.args.onuID, tt.args.uniID, tt.args.uni, tt.args.uniPort, tt.args.TpID, tt.args.UsMeterID, tt.args.DsMeterID, tt.args.flowMetadata)
+ switch tpInst := tpInst.(type) {
+ case *tp.TechProfile:
+ if tt.args.TpID != 64 {
+ t.Errorf("OpenOltFlowMgr.createTcontGemports() error = different tech, tech %v", tpInst)
+ }
+ case *tp.EponProfile:
+ if tt.args.TpID != 65 {
+ t.Errorf("OpenOltFlowMgr.createTcontGemports() error = different tech, tech %v", tpInst)
+ }
+ default:
+ t.Errorf("OpenOltFlowMgr.createTcontGemports() error = different tech, tech %v", tpInst)
+ }
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_RemoveFlow(t *testing.T) {
+ ctx := context.Background()
+ logger.Debug(ctx, "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)),
+ },
+ 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)
+
+ 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)
+
+ 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) {
+ if err := flowMgr.RemoveFlow(ctx, tt.args.flow); err != nil {
+ logger.Warn(ctx, err)
+ }
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_AddFlow(t *testing.T) {
+ kw := make(map[string]uint64)
+ kw["table_id"] = 1
+ kw["meter_id"] = 1
+ kw["write_metadata"] = 0x4000000000 // Tech-Profile-ID 64
+
+ fa := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(536870912),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
+ },
+ 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,
+ }
+
+ 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)),
+ },
+ 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,
+ }
+
+
+ fa4 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(1000),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Experimenter(257, []byte{1, 2, 3, 4}),
+ },
+ KV: kw,
+ }
+
+ fa5 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(1000),
+ fu.Metadata_ofp(1),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
+ },
+ Actions: []*ofp.OfpAction{
+ fu.Output(0),
+ },
+ KV: kw,
+ }
+
+ 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)),
+ },
+ 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)),
+ 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)),
+ 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,
+ }
+ 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)
+ // TODO: actually verify test cases
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_UpdateOnuInfo(t *testing.T) {
+ flwMgr := newMockFlowmgr()
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ wg := sync.WaitGroup{}
+
+ intfCount := 16
+ onuCount := 32
+
+ for i := 0; i < intfCount; i++ {
+ for j := 0; j < onuCount; j++ {
+ wg.Add(1)
+ go func(i uint32, j uint32) {
+ // TODO: actually verify success
+ _ = flwMgr.UpdateOnuInfo(ctx, i, i, fmt.Sprintf("onu-%d", i))
+ wg.Done()
+ }(uint32(i), uint32(j))
+ }
+
+ }
+
+ wg.Wait()
+}
+
+func TestOpenOltFlowMgr_addGemPortToOnuInfoMap(t *testing.T) {
+ flowMgr = newMockFlowmgr()
+ intfNum := 16
+ onuNum := 32
+
+ flowMgr.onuGemInfo = make(map[uint32][]rsrcMgr.OnuGemInfo, intfNum)
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ for i := 0; i < intfNum; i++ {
+ for o := 0; o < onuNum; o++ {
+ // TODO: actually verify success
+ _ = flowMgr.UpdateOnuInfo(ctx, uint32(i), uint32(o), fmt.Sprintf("i%do%d", i, o))
+ }
+ }
+
+ wg := sync.WaitGroup{}
+
+ for o := 0; o < onuNum; o++ {
+ for i := 0; i < intfNum; i++ {
+ wg.Add(1)
+ go func(intfId uint32, onuId uint32) {
+ gemID, _ := strconv.Atoi(fmt.Sprintf("90%d%d", intfId, onuId))
+
+ flowMgr.addGemPortToOnuInfoMap(ctx, intfId, onuId, uint32(gemID))
+ wg.Done()
+ }(uint32(i), uint32(o))
+ }
+ }
+
+ wg.Wait()
+
+ for i := 0; i < intfNum; i++ {
+ lenofOnu := len(flowMgr.onuGemInfo[uint32(i)])
+ if onuNum != lenofOnu {
+ t.Errorf("OnuGemInfo length is not as expected len = %d, want %d", lenofOnu, onuNum)
+ }
+
+ for o := 0; o < onuNum; o++ {
+ lenOfGemPorts := len(flowMgr.onuGemInfo[uint32(i)][o].GemPorts)
+ // check that each onuEntry has 1 gemPort
+ if lenOfGemPorts != 1 {
+ t.Errorf("Expected 1 GemPort per ONU, found %d", lenOfGemPorts)
+ }
+
+ // check that the value of the gemport is correct
+ gemID, _ := strconv.Atoi(fmt.Sprintf("90%d%d", i, o))
+ currentValue := flowMgr.onuGemInfo[uint32(i)][o].GemPorts[0]
+ if uint32(gemID) != currentValue {
+ t.Errorf("Expected GemPort value to be %d, found %d", gemID, currentValue)
+ }
+ }
+ }
+}
+
+func TestOpenOltFlowMgr_deleteGemPortFromLocalCache(t *testing.T) {
+ flwMgr := newMockFlowmgr()
+ type args struct {
+ intfID uint32
+ onuID uint32
+ gemPortIDs []uint32
+ gemPortIDsToBeDeleted []uint32
+ gemPortIDsRemaining []uint32
+ serialNum string
+ finalLength int
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ // Add/Delete single gem port
+ {"DeleteGemPortFromLocalCache1", args{0, 1, []uint32{1}, []uint32{1}, []uint32{}, "onu1", 0}},
+ // Delete all gemports
+ {"DeleteGemPortFromLocalCache2", args{0, 1, []uint32{1, 2, 3, 4}, []uint32{1, 2, 3, 4}, []uint32{}, "onu1", 0}},
+ // Try to delete when there is no gem port
+ {"DeleteGemPortFromLocalCache3", args{0, 1, []uint32{}, []uint32{1, 2}, []uint32{}, "onu1", 0}},
+ // Try to delete non-existent gem port
+ {"DeleteGemPortFromLocalCache4", args{0, 1, []uint32{1}, []uint32{2}, []uint32{1}, "onu1", 1}},
+ // Try to delete two of the gem ports
+ {"DeleteGemPortFromLocalCache5", args{0, 1, []uint32{1, 2, 3, 4}, []uint32{2, 4}, []uint32{1, 3}, "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) {
+ // TODO: should check returned errors are as expected?
+ _ = flwMgr.UpdateOnuInfo(ctx, tt.args.intfID, tt.args.onuID, tt.args.serialNum)
+ for _, gemPort := range tt.args.gemPortIDs {
+ flwMgr.addGemPortToOnuInfoMap(ctx, tt.args.intfID, tt.args.onuID, gemPort)
+ }
+ for _, gemPortDeleted := range tt.args.gemPortIDsToBeDeleted {
+ flwMgr.deleteGemPortFromLocalCache(ctx, tt.args.intfID, tt.args.onuID, gemPortDeleted)
+ }
+ lenofGemPorts := len(flwMgr.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)
+ }
+ gemPorts := flwMgr.onuGemInfo[tt.args.intfID][0].GemPorts
+ if !reflect.DeepEqual(tt.args.gemPortIDsRemaining, gemPorts) {
+ t.Errorf("GemPorts are not as expected = %v, want %v", gemPorts, tt.args.gemPortIDsRemaining)
+ }
+
+ })
+ }
+}
+
+func TestOpenOltFlowMgr_GetLogicalPortFromPacketIn(t *testing.T) {
+ flwMgr := 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 := flwMgr.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) {
+
+ untaggedStr := "01005e000002000000000001080046c00020000040000102fa140a000001e00000029404000017000705e10000fa"
+ untagged, err := hex.DecodeString(untaggedStr)
+ if err != nil {
+ t.Error("Unable to parse hex string", err)
+ panic(err)
+ }
+ singleTaggedStr := "01005e0000010025ba48172481000225080046c0002000004000010257deab140023e0000001940400001164ee9b0000000000000000000000000000"
+ singleTagged, err := hex.DecodeString(singleTaggedStr)
+ if err != nil {
+ t.Error("Unable to parse hex string", err)
+ panic(err)
+ }
+ doubleTaggedStr := "01005e000016deadbeefba11810002108100e030080046000028000000000102c5b87f000001e0000016940400002200f8030000000104000000e10000fa"
+ doubleTagged, err := hex.DecodeString(doubleTaggedStr)
+ if err != nil {
+ t.Error("Unable to parse hex string", err)
+ panic(err)
+ }
+
+ type args struct {
+ intfID uint32
+ onuID uint32
+ portNum uint32
+ packet []byte
+ }
+ tests := []struct {
+ name string
+ args args
+ want uint32
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ {"GetPacketOutGemPortID", args{intfID: 1, onuID: 1, portNum: 3, packet: untagged}, 3, false},
+ {"GetPacketOutGemPortID", args{intfID: 2, onuID: 2, portNum: 4, packet: singleTagged}, 4, false},
+ {"GetPacketOutGemPortID", args{intfID: 1, onuID: 2, portNum: 2, packet: doubleTagged}, 2, false},
+ {"GetPacketOutGemPortID", args{intfID: 1, onuID: 10, portNum: 10, packet: untagged}, 2, true},
+ {"GetPacketOutGemPortID", args{intfID: 1, onuID: 1, portNum: 3, packet: []byte{}}, 3, 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, tt.args.packet)
+ if tt.wantErr {
+ if err == nil {
+ //error expected but got value
+ t.Errorf("OpenOltFlowMgr.GetPacketOutGemPortID() = %v, wantErr %v", got, tt.wantErr)
+ }
+ } else {
+ if err != nil {
+ //error is not expected but got error
+ 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) {
+ 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) {
+ ctx := context.Background()
+ kw := make(map[string]uint64)
+ kw["table_id"] = 1
+ kw["meter_id"] = 1
+ kw["write_metadata"] = 0x4000000000 // Tech-Profile-ID 64
+
+ 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)),
+ },
+ 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,
+ }
+
+ 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,
+ }
+
+ fa3 := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(536870912),
+ fu.Metadata_ofp(1),
+ //fu.EthType(0x8100),
+ fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
+ },
+ Actions: []*ofp.OfpAction{
+ //fu.SetField(fu.Metadata_ofp(uint64(ofp.OfpInstructionType_OFPIT_WRITE_METADATA | 2))),
+ fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT))),
+ 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)),
+ 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))),
+ 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(ctx, classifierInfo, flowState)
+ formulateClassifierInfoFromFlow(ctx, classifierInfo2, flowState2)
+ formulateClassifierInfoFromFlow(ctx, classifierInfo3, flowState3)
+ formulateClassifierInfoFromFlow(ctx, classifierInfo4, flowState4)
+
+ err := formulateActionInfoFromFlow(ctx, 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(ctx, 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(ctx, 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(ctx, actionInfo4, classifierInfo4, flowState4)
+ if err != nil {
+ // Error logging is already done in the called function
+ // So just return in case of error
+ return
+ }
+
+
+ TpInst := &tp.TechProfile{
+ Name: "Test-Tech-Profile",
+ SubscriberIdentifier: "257",
+ ProfileType: "Mock",
+ Version: 1,
+ NumGemPorts: 4,
+ InstanceCtrl: tp.InstanceControl{
+ Onu: "1",
+ Uni: "16",
+ },
+ }
+
+ 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
+ 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_TestMulticastFlowAndGroup(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ group := newGroup(2, []uint32{1})
+ err := flowMgr.AddGroup(ctx, group)
+ if err != nil {
+ t.Error("group-add failed", err)
+ return
+ }
+ multicastFlowArgs := &fu.FlowArgs{
+ MatchFields: []*ofp.OfpOxmOfbField{
+ fu.InPort(1048576),
+ 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)
+ fmt.Println(ofpStats.Id)
+ err = flowMgr.AddFlow(ctx, ofpStats, &voltha.FlowMetadata{})
+ if err != nil {
+ t.Error("Multicast flow-add failed", err)
+ return
+ }
+
+ group = newGroup(2, []uint32{1, 2})
+ err = flowMgr.ModifyGroup(ctx, group)
+ if err != nil {
+ t.Error("modify-group failed", err)
+ return
+ }
+ err = flowMgr.RemoveFlow(ctx, ofpStats)
+ if err != nil {
+ t.Error("Multicast flow-remove failed", err)
+ return
+ }
+
+ err = flowMgr.DeleteGroup(ctx, group)
+ if err != nil {
+ t.Error("delete-group failed", err)
+ return
+ }
+}
diff --git a/internal/pkg/core/openolt_test.go b/internal/pkg/core/openolt_test.go
new file mode 100644
index 0000000..08c2f52
--- /dev/null
+++ b/internal/pkg/core/openolt_test.go
@@ -0,0 +1,938 @@
+/*
+ * 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"
+ "reflect"
+ "testing"
+
+ 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"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/olterrors"
+ 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"
+)
+
+// 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
+ KVStoreAddress string
+ KVStoreType string
+ exitChannel chan int
+ 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,
+ KVStoreAddress: testOlt.KVStoreAddress,
+ KVStoreType: testOlt.KVStoreType,
+ exitChannel: testOlt.exitChannel,
+ }
+}
+
+// mockDevice mocks Device.
+func mockDevice() *voltha.Device {
+ return &voltha.Device{
+ Id: "olt",
+ Root: true,
+ ParentId: "logical_device",
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ DeviceId: "olt",
+ DeviceType: "onu",
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ },
+ ConnectStatus: 1,
+ }
+}
+
+func TestNewOpenOLT(t *testing.T) {
+ tests := []struct {
+ name string
+ fields *fields
+ configFlags *config.AdapterFlags
+ want *OpenOLT
+ }{
+ {"newopenolt-1", &fields{}, &config.AdapterFlags{OnuNumber: 1, KVStoreAddress: "1.1.1.1:1", KVStoreType: "consul"},
+ &OpenOLT{numOnus: 1, KVStoreAddress: "1.1.1.1:1", KVStoreType: "consul"}},
+ {"newopenolt-2", &fields{}, &config.AdapterFlags{OnuNumber: 2, KVStoreAddress: "2.2.2.2:2", KVStoreType: "etcd"},
+ &OpenOLT{numOnus: 2, KVStoreAddress: "2.2.2.2:2", KVStoreType: "etcd"}},
+ {"newopenolt-3", &fields{}, &config.AdapterFlags{OnuNumber: 3, KVStoreAddress: "3.3.3.3:3", KVStoreType: "consul"},
+ &OpenOLT{numOnus: 3, KVStoreAddress: "3.3.3.3:3", KVStoreType: "consul"}},
+ }
+ 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{}, olterrors.ErrNotImplemented},
+ {"abandon_device-2", &fields{}, args{}, olterrors.ErrNotImplemented},
+ {"abandon_device-3", &fields{}, args{}, olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Abandon_device(context.Background(), 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"},
+ olterrors.ErrNotImplemented},
+ {"activate_image_upate-2", &fields{}, args{}, &voltha.ImageDownload{Id: "Image2-ABC123CDE"},
+ olterrors.ErrNotImplemented},
+ {"activate_image_upate-3", &fields{}, args{}, &voltha.ImageDownload{Id: "Image3-ABC123EFG"},
+ olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Activate_image_update(context.Background(), 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{}, olterrors.ErrNotImplemented},
+ {"adapter_descriptor-2", &fields{}, olterrors.ErrNotImplemented},
+ {"adapter_descriptor-3", &fields{}, olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Adapter_descriptor(context.Background()); 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 := olterrors.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(context.Background(), 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"},
+ olterrors.ErrNotImplemented},
+ {"cancel_image_download-2", &fields{}, args{}, &voltha.ImageDownload{Id: "Image2-ABC123IJK"},
+ olterrors.ErrNotImplemented},
+ {"cancel_image_download-3", &fields{}, args{}, &voltha.ImageDownload{Id: "Image3-ABC123KLM"},
+ olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Cancel_image_download(context.Background(), 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()},
+ olterrors.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(context.Background(), 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{},
+ olterrors.ErrNotImplemented},
+ {"device_types-2", &fields{}, &voltha.DeviceTypes{},
+ olterrors.ErrNotImplemented},
+ {"device_types-3", &fields{}, &voltha.DeviceTypes{},
+ olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Device_types(context.Background())
+ 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()},
+ olterrors.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(context.Background(), 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"},
+ olterrors.ErrNotImplemented},
+ {"download_image-2", &fields{}, args{}, &voltha.ImageDownload{Id: "Image2-ABC123LKJ"},
+ olterrors.ErrNotImplemented},
+ {"download_image-3", &fields{}, args{}, &voltha.ImageDownload{Id: "Image1-ABC123RTY"},
+ olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Download_image(context.Background(), 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{}, olterrors.ErrNotImplemented},
+ {"get_device_details-2", &fields{}, args{}, olterrors.ErrNotImplemented},
+ {"get_device_details-3", &fields{}, args{}, olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Get_device_details(context.Background(), 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"},
+ olterrors.ErrNotImplemented},
+ {"get_image_download_status-2", &fields{}, args{}, &voltha.ImageDownload{Id: "Image2-ABC123LKJ"},
+ olterrors.ErrNotImplemented},
+ {"get_image_download_status-3", &fields{}, args{}, &voltha.ImageDownload{Id: "Image1-ABC123DFG"},
+ olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Get_image_download_status(context.Background(), 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,
+ olterrors.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(context.Background(), 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_Health(t *testing.T) {
+ tests := []struct {
+ name string
+ fields *fields
+ want *voltha.HealthStatus
+ wantErr error
+ }{
+ {"health-1", &fields{}, &voltha.HealthStatus{}, olterrors.ErrNotImplemented},
+ {"health-2", &fields{}, &voltha.HealthStatus{}, olterrors.ErrNotImplemented},
+ {"health-3", &fields{}, &voltha.HealthStatus{}, olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Health(context.Background())
+ 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(&olterrors.ErrNotFound{})},
+ {"process_inter_adaptor_messgae-2", mockOlt(), message2,
+ reflect.TypeOf(&olterrors.ErrAdapter{})},
+ {"process_inter_adaptor_messgae-3", mockOlt(), message3,
+ reflect.TypeOf(&olterrors.ErrInvalidValue{})},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Process_inter_adapter_message(context.Background(), 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()},
+ olterrors.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(context.Background(), 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},
+ olterrors.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(context.Background(), 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 := olterrors.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(context.Background(), 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()},
+ olterrors.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(context.Background(), 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"},
+ olterrors.ErrNotImplemented},
+ {"revert_image_update-2", &fields{}, args{}, &voltha.ImageDownload{Id: "Image2-ABC123TYU"},
+ olterrors.ErrNotImplemented},
+ {"revert_image_update-3", &fields{}, args{}, &voltha.ImageDownload{Id: "Image3-ABC123GTH"},
+ olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ got, err := oo.Revert_image_update(context.Background(), 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{}, olterrors.ErrNotImplemented},
+ {"self_test_device-2", &fields{}, args{}, olterrors.ErrNotImplemented},
+ {"self_test_device-3", &fields{}, args{}, olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Self_test_device(context.Background(), 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)
+ if err := oo.Start(tt.args.ctx); err != nil {
+ t.Error(err)
+ }
+ 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{}, olterrors.ErrNotImplemented},
+ {"suppress_event-2", &fields{}, args{}, olterrors.ErrNotImplemented},
+ {"suppress_event-3", &fields{}, args{}, olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Suppress_event(context.Background(), 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{}, olterrors.ErrNotImplemented},
+ {"unsupress_event-2", &fields{}, args{}, olterrors.ErrNotImplemented},
+ {"unsupress_event-3", &fields{}, args{}, olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Unsuppress_event(context.Background(), 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{}, olterrors.ErrNotImplemented},
+ {"update_flows_bulk-2", &fields{}, args{}, olterrors.ErrNotImplemented},
+ {"update_flows_bulk-3", &fields{}, args{}, olterrors.ErrNotImplemented},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ oo := testOltObject(tt.fields)
+ if err := oo.Update_flows_bulk(context.Background(), 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()},
+ olterrors.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(context.Background(), 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", mockOlt(), args{device: mockDevice(), pmConfigs: &voltha.PmConfigs{DefaultFreq: 150, Grouped: false, FreqOverride: false}}, nil},
+ {"update_pm_config-2", &fields{}, args{device: mockDevice(), pmConfigs: &voltha.PmConfigs{DefaultFreq: 150, Grouped: false, FreqOverride: false}}, olterrors.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.Update_pm_config(context.Background(), tt.args.device, tt.args.pmConfigs); !reflect.DeepEqual(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(context.Background(), 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(context.Background(), 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 100644
index 0000000..d8fa663
--- /dev/null
+++ b/internal/pkg/core/statsmanager.go
@@ -0,0 +1,497 @@
+/*
+ * 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 (
+ "context"
+ "fmt"
+ "strconv"
+ "sync"
+ "time"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/olterrors"
+ "github.com/opencord/voltha-protos/v3/go/openolt"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+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
+}
+
+// NewOpenOltStatsMgr returns a new instance of the OpenOltStatisticsMgr
+func NewOpenOltStatsMgr(ctx context.Context, Dev *DeviceHandler) *OpenOltStatisticsMgr {
+
+ var StatMgr OpenOltStatisticsMgr
+
+ StatMgr.Device = Dev
+ var Ports interface{}
+ Ports, _ = InitPorts(ctx, "nni", Dev.device.Id, 1)
+ StatMgr.NorthBoundPort, _ = Ports.(map[uint32]*NniPort)
+ NumPonPorts := Dev.resourceMgr.DevInfo.GetPonPorts()
+ Ports, _ = InitPorts(ctx, "pon", Dev.device.Id, 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(ctx context.Context, 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(ctx, 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(ctx, i, "pon", DeviceID).(*PonPort)
+ PONPorts[PortNoToIntfID(PONPort.IntfID, voltha.Port_PON_OLT)] = PONPort
+ }
+ return PONPorts, nil
+ } else {
+ logger.Errorw(ctx, "invalid-type-of-interface", log.Fields{"interface-type": Intftype})
+ return nil, olterrors.NewErrInvalidValue(log.Fields{"interface-type": Intftype}, nil)
+ }
+}
+
+// BuildPortObject allows for updating north and southbound ports, newly discovered ports, and devices
+func BuildPortObject(ctx context.Context, 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:
+ */
+
+ if IntfType == "nni" {
+ IntfID := IntfIDToPortNo(PortNum, voltha.Port_ETHERNET_NNI)
+ nniID := PortNoToIntfID(IntfID, voltha.Port_ETHERNET_NNI)
+ logger.Debugw(ctx, "interface-type-nni",
+ log.Fields{
+ "nni-id": nniID,
+ "intf-type": IntfType})
+ 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)
+ logger.Debugw(ctx, "interface-type-pon",
+ log.Fields{
+ "pon-id": PONID,
+ "intf-type": IntfType})
+ return NewPONPort(PONID, DeviceID, IntfID, PortNum)
+ } else {
+ logger.Errorw(ctx, "invalid-type-of-interface", log.Fields{"intf-type": 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()
+ metricNames := StatMgr.Device.metrics.GetSubscriberMetrics()
+
+ var metrics []string
+ for metric := range metricNames {
+ if metricNames[metric].Enabled {
+ metrics = append(metrics, metric)
+ }
+ }
+
+ for _, mName := range metrics {
+ 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()
+ metricNames := StatMgr.Device.metrics.GetSubscriberMetrics()
+
+ var metrics []string
+ for metric := range metricNames {
+ if metricNames[metric].Enabled {
+ metrics = append(metrics, metric)
+ }
+ }
+
+ for _, mName := range metrics {
+ switch mName {
+ case "rx_bytes":
+ ponval["RxBytes"] = float32(cm.RxBytes)
+ case "rx_packets":
+ ponval["RxPackets"] = float32(cm.RxPackets)
+ 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)
+ case "tx_bytes":
+ ponval["TxBytes"] = float32(cm.TxBytes)
+ case "tx_packets":
+ ponval["TxPackets"] = float32(cm.TxPackets)
+ 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(ctx context.Context, val map[string]float32,
+ port *voltha.Port, devID string, devType string) {
+ logger.Debugw(ctx, "publish-metrics",
+ log.Fields{
+ "port": port.Label,
+ "metrics": val})
+
+ var metricInfo voltha.MetricInformation
+ var ke voltha.KpiEvent2
+ var volthaEventSubCatgry voltha.EventSubCategory_Types
+ metricsContext := make(map[string]string)
+ metricsContext["oltid"] = devID
+ metricsContext["devicetype"] = devType
+ metricsContext["portlabel"] = port.Label
+ metricsContext["portno"] = strconv.Itoa(int(port.PortNo))
+
+ if port.Type == voltha.Port_ETHERNET_NNI {
+ volthaEventSubCatgry = voltha.EventSubCategory_NNI
+ } else {
+ volthaEventSubCatgry = voltha.EventSubCategory_PON
+ }
+
+ raisedTs := time.Now().UnixNano()
+ mmd := voltha.MetricMetaData{
+ Title: port.Type.String(),
+ Ts: float64(raisedTs),
+ Context: metricsContext,
+ 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(ctx, "STATS_EVENT", &ke, voltha.EventCategory_EQUIPMENT, volthaEventSubCatgry, raisedTs); err != nil {
+ logger.Errorw(ctx, "failed-to-send-pon-stats", log.Fields{"err": err})
+ }
+}
+
+// PortStatisticsIndication handles the port statistics indication
+func (StatMgr *OpenOltStatisticsMgr) PortStatisticsIndication(ctx context.Context, PortStats *openolt.PortStatistics, NumPonPorts uint32) {
+ StatMgr.PortsStatisticsKpis(ctx, PortStats, NumPonPorts)
+ logger.Debugw(ctx, "received-port-stats-indication", log.Fields{"port-stats": PortStats})
+}
+
+// FlowStatisticsIndication to be implemented
+func FlowStatisticsIndication(ctx context.Context, self, FlowStats *openolt.FlowStatistics) {
+ logger.Debugw(ctx, "flow-stats-collected", log.Fields{"flow-stats": FlowStats})
+}
+
+// PortsStatisticsKpis map the port stats values into a dictionary, creates the kpiEvent and then publish to Kafka
+func (StatMgr *OpenOltStatisticsMgr) PortsStatisticsKpis(ctx context.Context, PortStats *openolt.PortStatistics, NumPonPorts uint32) {
+
+ /*map the port stats values into a dictionary
+ Create a kpoEvent and publish to Kafka
+
+ :param port_stats:
+ :return:
+ */
+ 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()
+ logger.Debugw(ctx, "received-nni-stats", log.Fields{"nni-stats": 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()
+ logger.Debugw(ctx, "received-pon-stats-for-port", log.Fields{"port-pon-stats": portPonStat})
+ }
+ }
+
+ /*
+ 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
+ */
+ /* 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) {
+ logger.Error(ctx, "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..0155330
--- /dev/null
+++ b/internal/pkg/core/statsmanager_test.go
@@ -0,0 +1,268 @@
+/*
+ * 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 (
+ "context"
+ "fmt"
+ "reflect"
+ "testing"
+
+ "github.com/opencord/voltha-protos/v3/go/openolt"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+func TestOpenOltStatisticsMgr_PortStatisticsIndication(t *testing.T) {
+ device := &voltha.Device{
+ Id: "olt",
+ Root: true,
+ ParentId: "logical_device",
+ ProxyAddress: &voltha.Device_ProxyAddress{
+ DeviceId: "olt",
+ DeviceType: "onu",
+ ChannelId: 1,
+ ChannelGroupId: 1,
+ },
+ ConnectStatus: 1,
+ }
+ dh := newMockDeviceHandler()
+ dh.device = device
+ StatMgr := NewOpenOltStatsMgr(context.Background(), 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(context.Background(), 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 {
+ val map[string]float32
+ port *voltha.Port
+ }
+ 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{
+ val: nval,
+ port: &voltha.Port{PortNo: 0, Label: fmt.Sprintf("%s%d", "nni-", 0), Type: voltha.Port_ETHERNET_NNI},
+ },
+ },
+ {
+ name: "PublishPONMetrics-1",
+ fields: fields{
+ Device: dhandlerPON,
+ NorthBoundPort: nil,
+ SouthBoundPort: ponmap,
+ },
+ args: args{
+ val: pval,
+ port: &voltha.Port{PortNo: 1, Label: fmt.Sprintf("%s%d", "pon-", 1), Type: voltha.Port_PON_OLT},
+ },
+ },
+ // 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(context.Background(), tt.args.val, tt.args.port, "onu1", "openolt")
+ })
+ }
+}
+
+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/core/status.go b/internal/pkg/core/status.go
new file mode 100644
index 0000000..6a3181e
--- /dev/null
+++ b/internal/pkg/core/status.go
@@ -0,0 +1,69 @@
+/*
+ * 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
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/core/l2oam"
+)
+
+var startWatchStatus bool = false
+
+// WatchStatus ovserves the setting file is updated or not
+func WatchStatus(ctx context.Context, cp adapterif.CoreProxy) {
+ if startWatchStatus {
+ return
+ }
+ startWatchStatus = true
+ logger.Debug(ctx, "Start WatchStatus()")
+ ticker := time.NewTicker(1000 * time.Millisecond)
+ for range ticker.C {
+ onuList, err := l2oam.ReadOnuStatusList()
+ if err == nil {
+ for i, onu := range onuList {
+ if onu.RebootState == "reboot" {
+ logger.Debug(ctx, fmt.Sprintf("WatchStatus() reboot: %s", onu.ID))
+ // reboot flag is set to off immediately
+ onuList[i].RebootState = ""
+
+ // find onu using MAC address
+ onuDevice := FindL2oamDevice(onu.MacAddress)
+ if onuDevice == nil {
+ logger.Debug(ctx, fmt.Sprintf("WatchStatus() device not found: %s", onu.ID))
+ continue
+ }
+
+ // send Reset ONU message
+ if err := onuDevice.send(l2oam.GenerateSetResetOnu(l2oam.OnuPkgType)); err != nil {
+ continue
+ }
+ _, err := onuDevice.waitResponse(ResponseTimer)
+ if err != nil {
+ logger.Error(ctx, fmt.Sprintf("[%s] reset ONU Send Error: %v", onuDevice.getDeviceName(), err))
+ continue
+ }
+ }
+ }
+ if err := l2oam.WriteOnuStatusList(onuList); err != nil {
+ continue
+ }
+ }
+ }
+}
diff --git a/internal/pkg/olterrors/common.go b/internal/pkg/olterrors/common.go
new file mode 100644
index 0000000..a4808f2
--- /dev/null
+++ b/internal/pkg/olterrors/common.go
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//Package olterrors Common Logger initialization
+package olterrors
+
+import (
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+var logger log.CLogger
+
+func init() {
+ var err error
+ logger, err = log.RegisterPackage(log.JSON, log.ErrorLevel, log.Fields{})
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/internal/pkg/olterrors/olterrors.go b/internal/pkg/olterrors/olterrors.go
new file mode 100644
index 0000000..0ec1089
--- /dev/null
+++ b/internal/pkg/olterrors/olterrors.go
@@ -0,0 +1,360 @@
+/*
+ * 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 olterrors implements functions to manipulate OLT errors
+package olterrors
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ "strings"
+)
+
+const (
+ defaultLogAndReturnLevel = log.ErrorLevel
+)
+
+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 {
+ ctx := context.Background()
+ var buf strings.Builder
+ _, er := buf.WriteString(e.name)
+ if er != nil {
+ logger.Error(ctx, er)
+ }
+ if len(e.fields) > 0 {
+ if val, err := json.Marshal(e.fields); err == nil {
+ _, er = buf.WriteString(fmt.Sprintf(": [%s]", string(val)))
+ if er != nil {
+ logger.Error(ctx, er)
+ }
+ }
+ }
+ if e.wrapped != nil {
+ _, er = buf.WriteString(fmt.Sprintf(": %s", e.wrapped.Error()))
+ if er != nil {
+ logger.Error(ctx, er)
+ }
+ }
+ 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 {
+ loggerFunc := logger.Debugw
+ switch level {
+ case log.InfoLevel:
+ loggerFunc = logger.Infow
+ case log.WarnLevel:
+ loggerFunc = logger.Warnw
+ case log.ErrorLevel:
+ loggerFunc = logger.Errorw
+ case log.FatalLevel:
+ loggerFunc = logger.Fatalw
+ }
+ local := e.fields
+ if e.wrapped != nil {
+ local = merge(e.fields, log.Fields{"wrapped": e.wrapped})
+ }
+ loggerFunc(context.Background(), 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
+}
+
+// ErrGroupOp represents an error condition when a group operation to a device did
+// not succeed
+type ErrGroupOp struct {
+ ErrAdapter
+}
+
+// NewErrGroupOp constructs a new error based on the given values
+func NewErrGroupOp(operation string, ID uint32, fields log.Fields, wrapped error) LoggableError {
+ return &ErrPersistence{
+ ErrAdapter{
+ name: "unable-to-perform-group-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 *ErrGroupOp) Log() error {
+ _ = e.ErrAdapter.Log()
+ return e
+}
+
+// LogAt logs the error at the specified level and then returns the error
+func (e *ErrGroupOp) 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 = NewErrAdapter("not-implemented", nil, nil)
+
+ ErrInvalidPortRange = NewErrAdapter("invalid-port-range", nil, nil)
+
+ ErrStateTransition = NewErrAdapter("state-transition", nil, nil)
+
+ ErrResourceManagerInstantiating = NewErrAdapter("resoure-manager-instantiating", nil, nil)
+)
diff --git a/internal/pkg/resourcemanager/common.go b/internal/pkg/resourcemanager/common.go
new file mode 100644
index 0000000..f21d7b2
--- /dev/null
+++ b/internal/pkg/resourcemanager/common.go
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Package resourcemanager Common Logger initialization
+package resourcemanager
+
+import (
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+var logger log.CLogger
+
+func init() {
+ var err error
+ logger, err = log.RegisterPackage(log.JSON, log.ErrorLevel, log.Fields{})
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/internal/pkg/resourcemanager/resourcemanager.go b/internal/pkg/resourcemanager/resourcemanager.go
new file mode 100644
index 0000000..389e09e
--- /dev/null
+++ b/internal/pkg/resourcemanager/resourcemanager.go
@@ -0,0 +1,1488 @@
+/*
+ * 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"
+ "sync"
+ "time"
+
+ "github.com/opencord/voltha-openolt-adapter/internal/pkg/olterrors"
+
+ "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 = 5 * time.Second
+ BasePathKvStore = "service/voltha/openolt/{%s}"
+ TpIDPathSuffix = "{%d,%d,%d}/tp_id"
+ MeterIDPathSuffix = "{%d,%d,%d}/{%d}/meter_id/{%s}"
+ NnniIntfID = "nniintfids"
+ OnuPacketINPathPrefix = "onu_packetin/{%d,%d,%d"
+ OnuPacketINPath = OnuPacketINPathPrefix + ",%d,%d}"
+ FlowIDsForGem = "flowids_per_gem/{%d}"
+ McastQueuesForIntf = "mcast_qs_for_int"
+ FlowGroup = "flow_groups/{%d}"
+ 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
+ VlanID uint16
+ Priority uint8
+}
+
+// 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
+ Address string // Host and port of the kv store to connect to
+ Args string // args
+ KVStore *db.Backend // backend kv store connection handle
+ DeviceType string
+ DevInfo *openolt.DeviceInfo // device information
+ ResourceMgrs map[uint32]*ponrmgr.PONResourceManager
+
+ GemPortIDMgmtLock []sync.RWMutex
+ AllocIDMgmtLock []sync.RWMutex
+ OnuIDMgmtLock []sync.RWMutex
+ FlowIDMgmtLock sync.RWMutex
+
+ flowIDToGemInfoLock sync.RWMutex
+}
+
+func newKVClient(ctx context.Context, storeType string, address string, timeout time.Duration) (kvstore.Client, error) {
+ logger.Infow(ctx, "kv-store-type", log.Fields{"store": storeType})
+ switch storeType {
+ case "consul":
+ return kvstore.NewConsulClient(ctx, address, timeout)
+ case "etcd":
+ return kvstore.NewEtcdClient(ctx, address, timeout, log.FatalLevel)
+ }
+ return nil, errors.New("unsupported-kv-store")
+}
+
+// SetKVClient sets the KV client and return a kv backend
+func SetKVClient(ctx context.Context, backend string, addr string, DeviceID string) *db.Backend {
+ kvClient, err := newKVClient(ctx, backend, addr, KvstoreTimeout)
+ if err != nil {
+ logger.Fatalw(ctx, "Failed to init KV client\n", log.Fields{"err": err})
+ return nil
+ }
+
+ kvbackend := &db.Backend{
+ Client: kvClient,
+ StoreType: backend,
+ Address: addr,
+ 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, KVStoreAddress string, kvStoreType string, deviceType string, devInfo *openolt.DeviceInfo) *OpenOltResourceMgr {
+ var ResourceMgr OpenOltResourceMgr
+ logger.Debugf(ctx, "Init new resource manager , address: %s, device-id: %s", KVStoreAddress, deviceID)
+ ResourceMgr.Address = KVStoreAddress
+ ResourceMgr.DeviceType = deviceType
+ ResourceMgr.DevInfo = devInfo
+ NumPONPorts := devInfo.GetPonPorts()
+
+ Backend := kvStoreType
+ ResourceMgr.KVStore = SetKVClient(ctx, Backend, ResourceMgr.Address, deviceID)
+ if ResourceMgr.KVStore == nil {
+ logger.Error(ctx, "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)
+
+ ResourceMgr.AllocIDMgmtLock = make([]sync.RWMutex, NumPONPorts)
+ ResourceMgr.GemPortIDMgmtLock = make([]sync.RWMutex, NumPONPorts)
+ ResourceMgr.OnuIDMgmtLock = make([]sync.RWMutex, NumPONPorts)
+
+
+ /*
+ 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()
+
+ 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)
+ }
+
+ var GlobalPONRsrcMgr *ponrmgr.PONResourceManager
+ var err error
+ for _, TechRange := range devInfo.Ranges {
+ technology := TechRange.Technology
+ logger.Debugf(ctx, "Device info technology %s", technology)
+ Ranges[technology] = TechRange
+
+ RsrcMgrsByTech[technology], err = ponrmgr.NewPONResourceManager(ctx, technology, deviceType, deviceID,
+ Backend, ResourceMgr.Address)
+ if err != nil {
+ logger.Errorf(ctx, "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)
+ }
+ for _, PONRMgr := range RsrcMgrsByTech {
+ _ = PONRMgr.InitDeviceResourcePool(ctx)
+ }
+ logger.Info(ctx, "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) {
+
+
+ logger.Debugf(ctx, "Resource range pool init for technology %s", ponRMgr.Technology)
+ status := ponRMgr.InitResourceRangesFromKVStore(ctx)
+ if !status {
+ logger.Debugf(ctx, "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
+ */
+ logger.Debugw(ctx, "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
+ }
+ }
+
+ logger.Debugw(ctx, "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(ctx, ONUIDStart, ONUIDEnd, ONUIDSharedPoolID,
+ AllocIDStart, AllocIDEnd, AllocIDSharedPoolID,
+ GEMPortIDStart, GEMPortIDEnd, GEMPortIDSharedPoolID,
+ FlowIDStart, FlowIDEnd, FlowIDSharedPoolID, 0, 1,
+ devInfo.PonPorts, techRange.IntfIds)
+
+
+ if ONUIDShared == openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH {
+ globalPONRMgr.UpdateRanges(ctx, ponrmgr.ONU_ID_START_IDX, ONUIDStart, ponrmgr.ONU_ID_END_IDX, ONUIDEnd,
+ "", 0, nil)
+ ponRMgr.UpdateRanges(ctx, 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(ctx, ponrmgr.ALLOC_ID_START_IDX, AllocIDStart, ponrmgr.ALLOC_ID_END_IDX, AllocIDEnd,
+ "", 0, nil)
+
+ ponRMgr.UpdateRanges(ctx, 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(ctx, ponrmgr.GEMPORT_ID_START_IDX, GEMPortIDStart, ponrmgr.GEMPORT_ID_END_IDX, GEMPortIDEnd,
+ "", 0, nil)
+ ponRMgr.UpdateRanges(ctx, 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(ctx, ponrmgr.FLOW_ID_START_IDX, FlowIDStart, ponrmgr.FLOW_ID_END_IDX, FlowIDEnd,
+ "", 0, nil)
+ ponRMgr.UpdateRanges(ctx, ponrmgr.FLOW_ID_START_IDX, FlowIDStart, ponrmgr.FLOW_ID_END_IDX, FlowIDEnd,
+ "", 0, globalPONRMgr)
+ }
+
+ ponRMgr.UpdateRanges(ctx, 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 {
+ logger.Debug(ctx, "Failed to clear device resource pool")
+ return err
+ }
+ }
+ logger.Debug(ctx, "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) {
+ RsrcMgr.OnuIDMgmtLock[ponIntfID].Lock()
+ defer RsrcMgr.OnuIDMgmtLock[ponIntfID].Unlock()
+
+ if _, ok := RsrcMgr.ResourceMgrs[ponIntfID]; !ok {
+ err := errors.New("invalid-pon-interface-" + strconv.Itoa(int(ponIntfID)))
+ return 0, err
+ }
+ ONUID, err := RsrcMgr.ResourceMgrs[ponIntfID].GetResourceID(ctx, ponIntfID,
+ ponrmgr.ONU_ID, 1)
+ if err != nil {
+ logger.Errorf(ctx, "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 {
+ logger.Errorw(ctx, "Error while getting flows from KV store", log.Fields{"flowId": flowID})
+ return nil
+ }
+ if len(flows) == 0 {
+ logger.Debugw(ctx, "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, vlanVid uint32, vlanPcp ...uint32) (uint32, error) {
+
+ var err error
+ FlowPath := fmt.Sprintf("%d,%d,%d", ponIntfID, ONUID, uniID)
+
+ RsrcMgr.FlowIDMgmtLock.Lock()
+ defer RsrcMgr.FlowIDMgmtLock.Unlock()
+
+ FlowIDs := RsrcMgr.ResourceMgrs[ponIntfID].GetCurrentFlowIDsForOnu(ctx, FlowPath)
+ if FlowIDs != nil {
+ logger.Debugw(ctx, "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(ctx, FlowInfo, flowID, gemportID, flowStoreCookie, flowCategory, vlanVid, vlanPcp...)
+ if er == nil {
+ logger.Debugw(ctx, "Found flowid for the vlan, pcp, and gem",
+ log.Fields{"flowID": flowID, "vlanVid": vlanVid, "vlanPcp": vlanPcp, "gemPortID": gemportID})
+ return flowID, er
+ }
+ }
+ }
+ logger.Debug(ctx, "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 {
+ logger.Errorf(ctx, "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)
+
+ RsrcMgr.AllocIDMgmtLock[intfID].Lock()
+ defer RsrcMgr.AllocIDMgmtLock[intfID].Unlock()
+
+ 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.
+ logger.Debugw(ctx, "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 {
+ logger.Error(ctx, "Failed to allocate alloc id")
+ return 0
+ }
+ err = RsrcMgr.ResourceMgrs[intfID].UpdateAllocIdsForOnu(ctx, IntfOnuIDUniID, AllocID)
+ if err != nil {
+ logger.Error(ctx, "Failed to update Alloc ID")
+ return 0
+ }
+ logger.Debugw(ctx, "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 {
+ logger.Errorf(ctx, "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 {
+ logger.Errorf(ctx, "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 {
+ logger.Error(ctx, "failed to Marshal")
+ return err
+ }
+
+ if err = RsrcMgr.KVStore.Put(ctx, IntfGEMPortPath, Val); err != nil {
+ logger.Errorf(ctx, "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 {
+ logger.Errorf(ctx, "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)
+
+ RsrcMgr.GemPortIDMgmtLock[ponPort].Lock()
+ defer RsrcMgr.GemPortIDMgmtLock[ponPort].Unlock()
+
+ 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 {
+ logger.Errorf(ctx, "Failed to get gem port id for %s", IntfOnuIDUniID)
+ return nil, err
+ }
+
+ err = RsrcMgr.ResourceMgrs[ponPort].UpdateGEMPortIDsForOnu(ctx, IntfOnuIDUniID,
+ GEMPortList)
+ if err != nil {
+ logger.Errorf(ctx, "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.OnuIDMgmtLock[intfID].Lock()
+ defer RsrcMgr.OnuIDMgmtLock[intfID].Unlock()
+
+ 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
+
+ RsrcMgr.FlowIDMgmtLock.Lock()
+ defer RsrcMgr.FlowIDMgmtLock.Unlock()
+
+ 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 {
+ logger.Errorw(ctx, "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.FlowIDMgmtLock.Lock()
+ defer RsrcMgr.FlowIDMgmtLock.Unlock()
+
+ 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 {
+ logger.Errorw(ctx, "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.AllocIDMgmtLock[IntfID].Lock()
+ defer RsrcMgr.AllocIDMgmtLock[IntfID].Unlock()
+
+ 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.GemPortIDMgmtLock[IntfID].Lock()
+ defer RsrcMgr.GemPortIDMgmtLock[IntfID].Unlock()
+
+ 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)
+
+ RsrcMgr.AllocIDMgmtLock[intfID].Lock()
+ AllocIDs := RsrcMgr.ResourceMgrs[intfID].GetCurrentAllocIDForOnu(ctx, IntfOnuIDUniID)
+
+ RsrcMgr.ResourceMgrs[intfID].FreeResourceID(ctx, intfID,
+ ponrmgr.ALLOC_ID,
+ AllocIDs)
+ RsrcMgr.AllocIDMgmtLock[intfID].Unlock()
+
+ RsrcMgr.GemPortIDMgmtLock[intfID].Lock()
+ GEMPortIDs := RsrcMgr.ResourceMgrs[intfID].GetCurrentGEMPortIDsForOnu(ctx, IntfOnuIDUniID)
+ RsrcMgr.ResourceMgrs[intfID].FreeResourceID(ctx, intfID,
+ ponrmgr.GEMPORT_ID,
+ GEMPortIDs)
+ RsrcMgr.GemPortIDMgmtLock[intfID].Unlock()
+
+ RsrcMgr.FlowIDMgmtLock.Lock()
+ FlowIDs := RsrcMgr.ResourceMgrs[intfID].GetCurrentFlowIDsForOnu(ctx, IntfOnuIDUniID)
+ RsrcMgr.ResourceMgrs[intfID].FreeResourceID(ctx, intfID,
+ ponrmgr.FLOW_ID,
+ FlowIDs)
+ RsrcMgr.FlowIDMgmtLock.Unlock()
+
+ RsrcMgr.ResourceMgrs[intfID].RemoveResourceMap(ctx, IntfOnuIDUniID)
+ 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 {
+ logger.Debugw(ctx, "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 {
+ logger.Debugw(ctx, "Found flows", log.Fields{"flows": *FlowInfo, "flowId": flowID})
+ for _, Info := range *FlowInfo {
+ if Info.FlowStoreCookie == flowStoreCookie {
+ logger.Debug(ctx, "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 {
+ logger.Errorw(ctx, "Failed to convert into byte array", log.Fields{"error": err})
+ return Data
+ }
+ if err = json.Unmarshal(Val, &Data); err != nil {
+ logger.Error(ctx, "Failed to unmarshal", log.Fields{"error": err})
+ return Data
+ }
+ }
+ } else {
+ logger.Errorf(ctx, "Failed to get TP id from kvstore for path %s", Path)
+ }
+ logger.Debugf(ctx, "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 {
+ logger.Errorw(ctx, "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 {
+ logger.Error(ctx, "failed to Marshal")
+ return err
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, IntfOnuUniID, Value); err != nil {
+ logger.Errorf(ctx, "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 {
+ logger.Debugf(ctx, "TpID %d is already in tpIdList for the path %s", TpID, IntfOnuUniID)
+ return err
+ }
+ }
+ logger.Debugf(ctx, "updating tp id %d on path %s", TpID, IntfOnuUniID)
+ tpIDList = append(tpIDList, TpID)
+ Value, err = json.Marshal(tpIDList)
+ if err != nil {
+ logger.Error(ctx, "failed to Marshal")
+ return err
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, IntfOnuUniID, Value); err != nil {
+ logger.Errorf(ctx, "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 {
+ logger.Error(ctx, "failed to Marshal meter config")
+ return err
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, IntfOnuUniID, Value); err != nil {
+ logger.Errorf(ctx, "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 {
+ logger.Debug(ctx, "Found meter in KV store", log.Fields{"Direction": Direction})
+ Val, er := kvstore.ToByte(Value.Value)
+ if er != nil {
+ logger.Errorw(ctx, "Failed to convert into byte array", log.Fields{"error": er})
+ return nil, er
+ }
+ if er = json.Unmarshal(Val, &meterConfig); er != nil {
+ logger.Error(ctx, "Failed to unmarshal meterconfig", log.Fields{"error": er})
+ return nil, er
+ }
+ } else {
+ logger.Debug(ctx, "meter-does-not-exists-in-KVStore")
+ return nil, err
+ }
+ } else {
+ logger.Errorf(ctx, "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 {
+ logger.Errorf(ctx, "Failed to delete meter id %s from kvstore ", Path)
+ return err
+ }
+ return nil
+}
+
+func getFlowIDFromFlowInfo(ctx context.Context, FlowInfo *[]FlowInfo, flowID, gemportID uint32, flowStoreCookie uint64, flowCategory string,
+ vlanVid uint32, vlanPcp ...uint32) error {
+ if FlowInfo != nil {
+ for _, Info := range *FlowInfo {
+ if int32(gemportID) == Info.Flow.GemportId && flowCategory != "" && Info.FlowCategory == flowCategory {
+ logger.Debug(ctx, "Found flow matching with flow category", log.Fields{"flowId": flowID, "FlowCategory": flowCategory})
+ if Info.FlowCategory == "HSIA_FLOW" {
+ if err := checkVlanAndPbitEqualityForFlows(vlanVid, Info, vlanPcp[0]); err == nil {
+ return nil
+ }
+ }
+ }
+ if int32(gemportID) == Info.Flow.GemportId && flowStoreCookie != 0 && Info.FlowStoreCookie == flowStoreCookie {
+ if flowCategory != "" && Info.FlowCategory == flowCategory {
+ logger.Debug(ctx, "Found flow matching with flow category", log.Fields{"flowId": flowID, "FlowCategory": flowCategory})
+ return nil
+ }
+ }
+ }
+ }
+ logger.Debugw(ctx, "the flow can be related to a different service", log.Fields{"flow_info": FlowInfo})
+ return errors.New("invalid flow-info")
+}
+
+func checkVlanAndPbitEqualityForFlows(vlanVid uint32, Info FlowInfo, vlanPcp uint32) error {
+ if err := checkVlanEqualityForFlows(vlanVid, Info); err != nil {
+ return err
+ }
+
+ if Info.Flow.Action.Cmd.RemarkInnerPbits || Info.Flow.Action.Cmd.RemarkOuterPbits {
+ if vlanPcp == Info.Flow.Action.OPbits || vlanPcp == Info.Flow.Action.IPbits {
+ return nil
+ }
+ } else if vlanPcp == Info.Flow.Classifier.OPbits {
+ //no remark action but flow has pbits
+ return nil
+ } else if vlanPcp == 0xff || Info.Flow.Classifier.OPbits == 0xff {
+ // no pbit found
+ return nil
+ }
+ return errors.New("not found in terms of pbit equality")
+}
+
+func checkVlanEqualityForFlows(vlanVid uint32, Info FlowInfo) error {
+ if vlanVid == Info.Flow.Action.OVid || vlanVid == Info.Flow.Classifier.IVid {
+ return nil
+ }
+ return errors.New("not found in terms of vlan_id equality")
+}
+
+//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 {
+ logger.Errorf(ctx, "failed to get onuifo for intfid %d", intfID)
+ return err
+ }
+ if len(onuGemData) == 0 {
+ logger.Errorw(ctx, "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 {
+ logger.Debugw(ctx, "Gem already present in onugem info, skpping addition", log.Fields{"gem": gem})
+ return nil
+ }
+ }
+ logger.Debugw(ctx, "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 {
+ logger.Error(ctx, "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 {
+ logger.Errorf(ctx, "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 {
+ logger.Errorf(ctx, "failed to get onuifo for intfid %d", IntfID)
+ return olterrors.NewErrPersistence("get", "OnuGemInfo", IntfID,
+ log.Fields{"onuGem": onuGem, "intfID": IntfID}, err)
+ }
+ onuGemData = append(onuGemData, onuGem)
+ err = RsrcMgr.ResourceMgrs[IntfID].AddOnuGemInfo(ctx, IntfID, onuGemData)
+ if err != nil {
+ logger.Error(ctx, "Failed to add onugem to kv store")
+ return olterrors.NewErrPersistence("set", "OnuGemInfo", IntfID,
+ log.Fields{"onuGemData": onuGemData, "intfID": IntfID}, err)
+ }
+
+ logger.Debugw(ctx, "added onu to 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 {
+ logger.Errorf(ctx, "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 {
+ logger.Debugw(ctx, "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 {
+ logger.Errorw(ctx, "Failed to add uin port in onugem to kv store", log.Fields{"uni": portNo})
+ return
+ }
+}
+
+//UpdateGemPortForPktIn updates gemport for pkt in path to kvstore, path being intfid, onuid, portno, vlan id, priority bit
+func (RsrcMgr *OpenOltResourceMgr) UpdateGemPortForPktIn(ctx context.Context, pktIn PacketInInfoKey, gemPort uint32) {
+
+ path := fmt.Sprintf(OnuPacketINPath, pktIn.IntfID, pktIn.OnuID, pktIn.LogicalPort, pktIn.VlanID, pktIn.Priority)
+ Value, err := json.Marshal(gemPort)
+ if err != nil {
+ logger.Error(ctx, "Failed to marshal data")
+ return
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, path, Value); err != nil {
+ logger.Errorw(ctx, "Failed to put to kvstore", log.Fields{"path": path, "value": gemPort})
+ return
+ }
+ logger.Debugw(ctx, "added gem packet in successfully", log.Fields{"path": path, "gem": gemPort})
+}
+
+// GetGemPortFromOnuPktIn gets the gem port from onu pkt in path, path being intfid, onuid, portno, vlan id, priority bit
+func (RsrcMgr *OpenOltResourceMgr) GetGemPortFromOnuPktIn(ctx context.Context, packetInInfoKey PacketInInfoKey) (uint32, error) {
+
+ var Val []byte
+ var gemPort uint32
+
+ path := fmt.Sprintf(OnuPacketINPath, packetInInfoKey.IntfID, packetInInfoKey.OnuID, packetInInfoKey.LogicalPort,
+ packetInInfoKey.VlanID, packetInInfoKey.Priority)
+
+ value, err := RsrcMgr.KVStore.Get(ctx, path)
+ if err != nil {
+ logger.Errorw(ctx, "Failed to get from kv store", log.Fields{"path": path})
+ return uint32(0), err
+ } else if value == nil {
+ logger.Debugw(ctx, "No pkt in gem found", log.Fields{"path": path})
+ return uint32(0), nil
+ }
+
+ if Val, err = kvstore.ToByte(value.Value); err != nil {
+ logger.Error(ctx, "Failed to convert to byte array")
+ return uint32(0), err
+ }
+ if err = json.Unmarshal(Val, &gemPort); err != nil {
+ logger.Error(ctx, "Failed to unmarshall")
+ return uint32(0), err
+ }
+ logger.Debugw(ctx, "found packein gemport from path", log.Fields{"path": path, "gem": gemPort})
+
+ return gemPort, nil
+}
+
+//DelGemPortPktInOfAllServices deletes the gemports from pkt in path for all services
+func (RsrcMgr *OpenOltResourceMgr) DelGemPortPktInOfAllServices(ctx context.Context, intfID uint32, onuID uint32, logicalPort uint32) error {
+
+ Path := fmt.Sprintf(OnuPacketINPathPrefix, intfID, onuID, logicalPort)
+ logger.Debugf(ctx, "getting flows from the path:%s", Path)
+ Value, err := RsrcMgr.KVStore.List(ctx, Path)
+ if err != nil {
+ logger.Errorf(ctx, "failed to get flows from kvstore for path %s", Path)
+ return errors.New("failed to get flows from kvstore for path " + Path)
+ }
+ logger.Debugf(ctx, "%d flows retrieved from the path:%s", len(Value), Path)
+
+ for key := range Value {
+ if err := RsrcMgr.KVStore.Delete(ctx, key); err != nil {
+ logger.Errorf(ctx, "Falied to remove resource %s", key)
+ 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 {
+ logger.Errorw(ctx, "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 := NnniIntfID
+ value, err := RsrcMgr.KVStore.Get(ctx, path)
+ if err != nil {
+ logger.Error(ctx, "failed to get data from kv store")
+ return nil, err
+ }
+ if value != nil {
+ if Val, err = kvstore.ToByte(value.Value); err != nil {
+ logger.Error(ctx, "Failed to convert to byte array")
+ return nil, err
+ }
+ if err = json.Unmarshal(Val, &nni); err != nil {
+ logger.Error(ctx, "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 {
+ logger.Error(ctx, "failed to fetch nni interfaces from kv store")
+ return err
+ }
+
+ path := NnniIntfID
+ nni = append(nni, nniIntf)
+ Value, err = json.Marshal(nni)
+ if err != nil {
+ logger.Error(ctx, "Failed to marshal data")
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, path, Value); err != nil {
+ logger.Errorw(ctx, "Failed to put to kvstore", log.Fields{"path": path, "value": Value})
+ return err
+ }
+ logger.Debugw(ctx, "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 := NnniIntfID
+
+ if err := RsrcMgr.KVStore.Delete(ctx, path); err != nil {
+ logger.Errorw(ctx, "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 {
+ logger.Error(ctx, "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 {
+ logger.Error(ctx, "Failed to marshal data", log.Fields{"error": err})
+ return err
+ }
+
+ RsrcMgr.flowIDToGemInfoLock.Lock()
+ defer RsrcMgr.flowIDToGemInfoLock.Unlock()
+ if err = RsrcMgr.KVStore.Put(ctx, path, val); err != nil {
+ logger.Errorw(ctx, "Failed to put to kvstore", log.Fields{"error": err, "path": path, "value": val})
+ return err
+ }
+ logger.Debugw(ctx, "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 {
+ logger.Error(ctx, "Failed to ger flowids for interface", log.Fields{"error": err, "intf": intf})
+ return
+ }
+ if flowsForGem == nil {
+ logger.Error(ctx, "No flowids found ", log.Fields{"intf": intf, "gemport": gem})
+ return
+ }
+ delete(flowsForGem, gem)
+ val, err = json.Marshal(flowsForGem)
+ if err != nil {
+ logger.Error(ctx, "Failed to marshal data", log.Fields{"error": err})
+ return
+ }
+
+ RsrcMgr.flowIDToGemInfoLock.Lock()
+ defer RsrcMgr.flowIDToGemInfoLock.Unlock()
+ if err = RsrcMgr.KVStore.Put(ctx, path, val); err != nil {
+ logger.Errorw(ctx, "Failed to put to kvstore", log.Fields{"error": err, "path": path, "value": val})
+ }
+}
+
+//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
+ RsrcMgr.flowIDToGemInfoLock.RLock()
+ value, err := RsrcMgr.KVStore.Get(ctx, path)
+ RsrcMgr.flowIDToGemInfoLock.RUnlock()
+ if err != nil {
+ logger.Error(ctx, "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 {
+ logger.Error(ctx, "Failed to convert to byte array ", log.Fields{"error": err})
+ return nil, err
+ }
+ if err = json.Unmarshal(val, &flowsForGem); err != nil {
+ logger.Error(ctx, "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)
+ RsrcMgr.flowIDToGemInfoLock.Lock()
+ defer RsrcMgr.flowIDToGemInfoLock.Unlock()
+ if err := RsrcMgr.KVStore.Delete(ctx, path); err != nil {
+ logger.Errorw(ctx, "Failed to delete nni interfaces from kv store", log.Fields{"path": path})
+ }
+}
+
+// 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 := McastQueuesForIntf
+ var mcastQueueToIntfMap map[uint32][]uint32
+ var val []byte
+
+ kvPair, err := RsrcMgr.KVStore.Get(ctx, path)
+ if err != nil {
+ logger.Error(ctx, "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 {
+ logger.Error(ctx, "Failed to convert to byte array ", log.Fields{"error": err})
+ return nil, err
+ }
+ if err = json.Unmarshal(val, &mcastQueueToIntfMap); err != nil {
+ logger.Error(ctx, "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 := McastQueuesForIntf
+
+ mcastQueues, err := RsrcMgr.GetMcastQueuePerInterfaceMap(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "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 {
+ logger.Errorw(ctx, "Failed to marshal data", log.Fields{"error": err})
+ return err
+ }
+ if err = RsrcMgr.KVStore.Put(ctx, path, val); err != nil {
+ logger.Errorw(ctx, "Failed to put to kvstore", log.Fields{"error": err, "path": path, "value": val})
+ return err
+ }
+ logger.Debugw(ctx, "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)
+ }
+ 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 {
+ logger.Error(ctx, "failed to Marshal flow group object")
+ return err
+ }
+
+ if err = RsrcMgr.KVStore.Put(ctx, path, Value); err != nil {
+ logger.Errorf(ctx, "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) error {
+ 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 {
+ logger.Errorf(ctx, "Failed to remove resource %s due to %s", path, err)
+ return err
+ }
+ return nil
+}
+
+//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 {
+ logger.Errorw(ctx, "Failed to convert flow group into byte array", log.Fields{"error": err})
+ return false, groupInfo, err
+ }
+ if err = json.Unmarshal(Val, &groupInfo); err != nil {
+ logger.Errorw(ctx, "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..81e0fa4
--- /dev/null
+++ b/internal/pkg/resourcemanager/resourcemanager_test.go
@@ -0,0 +1,1188 @@
+/*
+ * 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"
+ "sync"
+ "testing"
+ "time"
+)
+
+func init() {
+ _, _ = log.SetDefaultLogger(log.JSON, log.DebugLevel, nil)
+}
+
+const (
+ MeterConfig = "meter_id"
+ FlowIDInfo = "flow_id_info"
+ FlowIDs = "flow_ids"
+ GemportIDs = "gemport_ids"
+ AllocIDs = "alloc_ids"
+ GemportIDPool = "gemport_id_pool"
+ AllocIDPool = "alloc_id_pool"
+ FlowIDpool = "flow_id_pool"
+)
+
+// fields mocks OpenOltResourceMgr struct.
+type fields struct {
+ DeviceID string
+ Address string
+ Args string
+ KVStore *db.Backend
+ DeviceType string
+ DevInfo *openolt.DeviceInfo
+ ResourceMgrs map[uint32]*ponrmgr.PONResourceManager
+ NumOfPonPorts uint32
+}
+
+// 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)
+ resMgr.NumOfPonPorts = 2
+ 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) {
+ logger.Debugw(ctx, "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) {
+ logger.Debug(ctx, "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) {
+ logger.Debug(ctx, "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) {
+ logger.Debug(ctx, "Error Error Error Key:", AllocIDs, GemportIDs)
+ str, _ := json.Marshal(1)
+ return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
+ }
+ if strings.Contains(key, McastQueuesForIntf) {
+ logger.Debug(ctx, "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 time.Duration) (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 time.Duration) 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(ctx context.Context, key string, ch chan *kvstore.Event) {
+}
+
+// Close mock function implementation for KVClient
+func (kvclient *MockResKVClient) Close(ctx context.Context) {
+}
+
+// testResMgrObject maps fields type to OpenOltResourceMgr type.
+func testResMgrObject(testResMgr *fields) *OpenOltResourceMgr {
+ var rsrMgr = OpenOltResourceMgr{
+ DeviceID: testResMgr.DeviceID,
+ Args: testResMgr.Args,
+ KVStore: testResMgr.KVStore,
+ DeviceType: testResMgr.DeviceType,
+ Address: testResMgr.Address,
+ DevInfo: testResMgr.DevInfo,
+ ResourceMgrs: testResMgr.ResourceMgrs,
+ }
+
+ rsrMgr.AllocIDMgmtLock = make([]sync.RWMutex, testResMgr.NumOfPonPorts)
+ rsrMgr.GemPortIDMgmtLock = make([]sync.RWMutex, testResMgr.NumOfPonPorts)
+ rsrMgr.OnuIDMgmtLock = make([]sync.RWMutex, testResMgr.NumOfPonPorts)
+
+ return &rsrMgr
+}
+
+func TestNewResourceMgr(t *testing.T) {
+ type args struct {
+ deviceID string
+ KVStoreAddress 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.KVStoreAddress, 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{1, 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{1, 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{1, 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{1, []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{1, 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{1, 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{1, 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{1, 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
+ vlanVid uint32
+ vlanPcp []uint32
+ }
+ tests := []struct {
+ name string
+ fields *fields
+ args args
+ want uint32
+ wantErr error
+ }{
+ {"GetFlowID-1", getResMgr(), args{1, 2, 2, 2, 2,
+ "HSIA", 33, 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.vlanVid, 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{1, 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", 0, 1, 1, 64},
+ &ofp.OfpMeterConfig{}, errors.New("failed to get Meter config from kvstore for path")},
+ {"GetMeterIDOnu", getResMgr(), args{"DOWNSTREAM", 1, 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{1}, 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{1, 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{1, 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{1, 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{1, 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{1, 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{1, 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},
+ 1, 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", 1, 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{1, 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
+ address string
+ 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(context.Background(), tt.args.backend, tt.args.address, 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
+ vlanVid uint32
+ vlanPcp []uint32
+ }
+ flowInfo := &[]FlowInfo{
+ {
+ &openolt.Flow{
+ FlowId: 1,
+ GemportId: 1,
+ Classifier: &openolt.Classifier{
+ OPbits: 1,
+ OVid: 33,
+ },
+ Action: &openolt.Action{
+ Cmd: &openolt.ActionCmd{
+ AddOuterTag: true,
+ },
+ OVid: 7,
+ },
+ },
+ 1,
+ "HSIA_FLOW",
+ 2000,
+ },
+ {
+ &openolt.Flow{
+ GemportId: 1,
+ Classifier: &openolt.Classifier{
+ OVid: 0,
+ },
+ Action: &openolt.Action{
+ Cmd: &openolt.ActionCmd{
+ TrapToHost: true,
+ },
+ },
+ },
+ 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", 33, []uint32{1, 2}}, errors.New("invalid flow-info")},
+ {"getFlowIdFromFlowInfo-2", args{flowInfo, 1, 1, 1,
+ "EAPOL", 33, []uint32{1, 2}}, errors.New("invalid flow-info")},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ err := getFlowIDFromFlowInfo(context.Background(), tt.args.FlowInfo, tt.args.flowID, tt.args.gemportID, tt.args.flowStoreCookie, tt.args.flowCategory, tt.args.vlanVid, 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 time.Duration
+ }
+ 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(context.Background(), 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
+ }
+ group1 := newGroup(1, []uint32{1})
+ group2 := newGroup(2, []uint32{2})
+ tests := []struct {
+ name string
+ args args
+ fields *fields
+ }{
+ {"AddFlowGroupToKVStore-1", args{group1, true}, getResMgr()},
+ {"AddFlowGroupToKVStore-2", args{group2, false}, 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.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
+ }
+ tests := []struct {
+ name string
+ args args
+ fields *fields
+ }{
+ {"RemoveFlowGroupFromKVStore-1", args{1, true}, getResMgr()},
+ {"RemoveFlowGroupFromKVStore-2", args{2, false}, 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.RemoveFlowGroupFromKVStore(ctx, tt.args.groupID, tt.args.cached)
+ if err != nil {
+ t.Errorf("%s got false but wants true", tt.name)
+ return
+ }
+ })
+ }
+}
+
+func TestOpenOltResourceMgr_GetFlowGroupFromKVStore(t *testing.T) {
+ type args struct {
+ groupID uint32
+ cached bool
+ }
+ 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()},
+ }
+ 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
+ }
+ })
+ }
+}