[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
+			}
+		})
+	}
+}